gitlab-rdoc 6.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (196) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.rdoc +220 -0
  3. data/CVE-2013-0256.rdoc +49 -0
  4. data/ExampleMarkdown.md +37 -0
  5. data/ExampleRDoc.rdoc +208 -0
  6. data/Gemfile +12 -0
  7. data/History.rdoc +1666 -0
  8. data/LEGAL.rdoc +50 -0
  9. data/LICENSE.rdoc +57 -0
  10. data/README.rdoc +133 -0
  11. data/RI.rdoc +57 -0
  12. data/Rakefile +101 -0
  13. data/TODO.rdoc +59 -0
  14. data/bin/console +7 -0
  15. data/bin/setup +6 -0
  16. data/exe/rdoc +44 -0
  17. data/exe/ri +12 -0
  18. data/lib/rdoc/alias.rb +112 -0
  19. data/lib/rdoc/anon_class.rb +11 -0
  20. data/lib/rdoc/any_method.rb +361 -0
  21. data/lib/rdoc/attr.rb +176 -0
  22. data/lib/rdoc/class_module.rb +802 -0
  23. data/lib/rdoc/code_object.rb +421 -0
  24. data/lib/rdoc/code_objects.rb +6 -0
  25. data/lib/rdoc/comment.rb +250 -0
  26. data/lib/rdoc/constant.rb +187 -0
  27. data/lib/rdoc/context/section.rb +232 -0
  28. data/lib/rdoc/context.rb +1266 -0
  29. data/lib/rdoc/cross_reference.rb +202 -0
  30. data/lib/rdoc/encoding.rb +136 -0
  31. data/lib/rdoc/erb_partial.rb +19 -0
  32. data/lib/rdoc/erbio.rb +42 -0
  33. data/lib/rdoc/extend.rb +10 -0
  34. data/lib/rdoc/generator/darkfish.rb +790 -0
  35. data/lib/rdoc/generator/json_index.rb +300 -0
  36. data/lib/rdoc/generator/markup.rb +160 -0
  37. data/lib/rdoc/generator/pot/message_extractor.rb +68 -0
  38. data/lib/rdoc/generator/pot/po.rb +84 -0
  39. data/lib/rdoc/generator/pot/po_entry.rb +141 -0
  40. data/lib/rdoc/generator/pot.rb +98 -0
  41. data/lib/rdoc/generator/ri.rb +31 -0
  42. data/lib/rdoc/generator/template/darkfish/.document +0 -0
  43. data/lib/rdoc/generator/template/darkfish/_footer.rhtml +5 -0
  44. data/lib/rdoc/generator/template/darkfish/_head.rhtml +22 -0
  45. data/lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml +19 -0
  46. data/lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml +9 -0
  47. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +15 -0
  48. data/lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml +9 -0
  49. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +15 -0
  50. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +15 -0
  51. data/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml +12 -0
  52. data/lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml +11 -0
  53. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +12 -0
  54. data/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml +11 -0
  55. data/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml +14 -0
  56. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +11 -0
  57. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +18 -0
  58. data/lib/rdoc/generator/template/darkfish/class.rhtml +172 -0
  59. data/lib/rdoc/generator/template/darkfish/css/fonts.css +167 -0
  60. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +639 -0
  61. data/lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf +0 -0
  62. data/lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf +0 -0
  63. data/lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf +0 -0
  64. data/lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf +0 -0
  65. data/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf +0 -0
  66. data/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf +0 -0
  67. data/lib/rdoc/generator/template/darkfish/images/add.png +0 -0
  68. data/lib/rdoc/generator/template/darkfish/images/arrow_up.png +0 -0
  69. data/lib/rdoc/generator/template/darkfish/images/brick.png +0 -0
  70. data/lib/rdoc/generator/template/darkfish/images/brick_link.png +0 -0
  71. data/lib/rdoc/generator/template/darkfish/images/bug.png +0 -0
  72. data/lib/rdoc/generator/template/darkfish/images/bullet_black.png +0 -0
  73. data/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png +0 -0
  74. data/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png +0 -0
  75. data/lib/rdoc/generator/template/darkfish/images/date.png +0 -0
  76. data/lib/rdoc/generator/template/darkfish/images/delete.png +0 -0
  77. data/lib/rdoc/generator/template/darkfish/images/find.png +0 -0
  78. data/lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif +0 -0
  79. data/lib/rdoc/generator/template/darkfish/images/macFFBgHack.png +0 -0
  80. data/lib/rdoc/generator/template/darkfish/images/package.png +0 -0
  81. data/lib/rdoc/generator/template/darkfish/images/page_green.png +0 -0
  82. data/lib/rdoc/generator/template/darkfish/images/page_white_text.png +0 -0
  83. data/lib/rdoc/generator/template/darkfish/images/page_white_width.png +0 -0
  84. data/lib/rdoc/generator/template/darkfish/images/plugin.png +0 -0
  85. data/lib/rdoc/generator/template/darkfish/images/ruby.png +0 -0
  86. data/lib/rdoc/generator/template/darkfish/images/tag_blue.png +0 -0
  87. data/lib/rdoc/generator/template/darkfish/images/tag_green.png +0 -0
  88. data/lib/rdoc/generator/template/darkfish/images/transparent.png +0 -0
  89. data/lib/rdoc/generator/template/darkfish/images/wrench.png +0 -0
  90. data/lib/rdoc/generator/template/darkfish/images/wrench_orange.png +0 -0
  91. data/lib/rdoc/generator/template/darkfish/images/zoom.png +0 -0
  92. data/lib/rdoc/generator/template/darkfish/index.rhtml +22 -0
  93. data/lib/rdoc/generator/template/darkfish/js/darkfish.js +84 -0
  94. data/lib/rdoc/generator/template/darkfish/js/search.js +110 -0
  95. data/lib/rdoc/generator/template/darkfish/page.rhtml +18 -0
  96. data/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml +18 -0
  97. data/lib/rdoc/generator/template/darkfish/servlet_root.rhtml +62 -0
  98. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +58 -0
  99. data/lib/rdoc/generator/template/json_index/.document +1 -0
  100. data/lib/rdoc/generator/template/json_index/js/navigation.js +105 -0
  101. data/lib/rdoc/generator/template/json_index/js/searcher.js +229 -0
  102. data/lib/rdoc/generator.rb +51 -0
  103. data/lib/rdoc/ghost_method.rb +7 -0
  104. data/lib/rdoc/i18n/locale.rb +102 -0
  105. data/lib/rdoc/i18n/text.rb +126 -0
  106. data/lib/rdoc/i18n.rb +10 -0
  107. data/lib/rdoc/include.rb +10 -0
  108. data/lib/rdoc/known_classes.rb +73 -0
  109. data/lib/rdoc/markdown/entities.rb +2132 -0
  110. data/lib/rdoc/markdown/literals.kpeg +23 -0
  111. data/lib/rdoc/markdown/literals.rb +417 -0
  112. data/lib/rdoc/markdown.kpeg +1237 -0
  113. data/lib/rdoc/markdown.rb +16685 -0
  114. data/lib/rdoc/markup/attr_changer.rb +23 -0
  115. data/lib/rdoc/markup/attr_span.rb +36 -0
  116. data/lib/rdoc/markup/attribute_manager.rb +409 -0
  117. data/lib/rdoc/markup/attributes.rb +71 -0
  118. data/lib/rdoc/markup/blank_line.rb +28 -0
  119. data/lib/rdoc/markup/block_quote.rb +15 -0
  120. data/lib/rdoc/markup/document.rb +165 -0
  121. data/lib/rdoc/markup/formatter.rb +266 -0
  122. data/lib/rdoc/markup/hard_break.rb +32 -0
  123. data/lib/rdoc/markup/heading.rb +79 -0
  124. data/lib/rdoc/markup/include.rb +43 -0
  125. data/lib/rdoc/markup/indented_paragraph.rb +48 -0
  126. data/lib/rdoc/markup/list.rb +102 -0
  127. data/lib/rdoc/markup/list_item.rb +100 -0
  128. data/lib/rdoc/markup/paragraph.rb +29 -0
  129. data/lib/rdoc/markup/parser.rb +575 -0
  130. data/lib/rdoc/markup/pre_process.rb +296 -0
  131. data/lib/rdoc/markup/raw.rb +70 -0
  132. data/lib/rdoc/markup/regexp_handling.rb +41 -0
  133. data/lib/rdoc/markup/rule.rb +21 -0
  134. data/lib/rdoc/markup/table.rb +47 -0
  135. data/lib/rdoc/markup/to_ansi.rb +94 -0
  136. data/lib/rdoc/markup/to_bs.rb +77 -0
  137. data/lib/rdoc/markup/to_html.rb +444 -0
  138. data/lib/rdoc/markup/to_html_crossref.rb +176 -0
  139. data/lib/rdoc/markup/to_html_snippet.rb +285 -0
  140. data/lib/rdoc/markup/to_joined_paragraph.rb +47 -0
  141. data/lib/rdoc/markup/to_label.rb +75 -0
  142. data/lib/rdoc/markup/to_markdown.rb +192 -0
  143. data/lib/rdoc/markup/to_rdoc.rb +362 -0
  144. data/lib/rdoc/markup/to_table_of_contents.rb +89 -0
  145. data/lib/rdoc/markup/to_test.rb +70 -0
  146. data/lib/rdoc/markup/to_tt_only.rb +121 -0
  147. data/lib/rdoc/markup/verbatim.rb +84 -0
  148. data/lib/rdoc/markup.rb +867 -0
  149. data/lib/rdoc/meta_method.rb +7 -0
  150. data/lib/rdoc/method_attr.rb +419 -0
  151. data/lib/rdoc/mixin.rb +121 -0
  152. data/lib/rdoc/normal_class.rb +93 -0
  153. data/lib/rdoc/normal_module.rb +74 -0
  154. data/lib/rdoc/options.rb +1285 -0
  155. data/lib/rdoc/parser/c.rb +1225 -0
  156. data/lib/rdoc/parser/changelog.rb +335 -0
  157. data/lib/rdoc/parser/markdown.rb +24 -0
  158. data/lib/rdoc/parser/rd.rb +23 -0
  159. data/lib/rdoc/parser/ripper_state_lex.rb +590 -0
  160. data/lib/rdoc/parser/ruby.rb +2327 -0
  161. data/lib/rdoc/parser/ruby_tools.rb +167 -0
  162. data/lib/rdoc/parser/simple.rb +61 -0
  163. data/lib/rdoc/parser/text.rb +12 -0
  164. data/lib/rdoc/parser.rb +277 -0
  165. data/lib/rdoc/rd/block_parser.rb +1056 -0
  166. data/lib/rdoc/rd/block_parser.ry +639 -0
  167. data/lib/rdoc/rd/inline.rb +72 -0
  168. data/lib/rdoc/rd/inline_parser.rb +1208 -0
  169. data/lib/rdoc/rd/inline_parser.ry +593 -0
  170. data/lib/rdoc/rd.rb +100 -0
  171. data/lib/rdoc/rdoc.rb +579 -0
  172. data/lib/rdoc/require.rb +52 -0
  173. data/lib/rdoc/ri/driver.rb +1572 -0
  174. data/lib/rdoc/ri/formatter.rb +6 -0
  175. data/lib/rdoc/ri/paths.rb +171 -0
  176. data/lib/rdoc/ri/store.rb +7 -0
  177. data/lib/rdoc/ri/task.rb +71 -0
  178. data/lib/rdoc/ri.rb +21 -0
  179. data/lib/rdoc/rubygems_hook.rb +246 -0
  180. data/lib/rdoc/servlet.rb +451 -0
  181. data/lib/rdoc/single_class.rb +26 -0
  182. data/lib/rdoc/stats/normal.rb +58 -0
  183. data/lib/rdoc/stats/quiet.rb +60 -0
  184. data/lib/rdoc/stats/verbose.rb +46 -0
  185. data/lib/rdoc/stats.rb +462 -0
  186. data/lib/rdoc/store.rb +979 -0
  187. data/lib/rdoc/task.rb +329 -0
  188. data/lib/rdoc/text.rb +304 -0
  189. data/lib/rdoc/token_stream.rb +119 -0
  190. data/lib/rdoc/tom_doc.rb +263 -0
  191. data/lib/rdoc/top_level.rb +289 -0
  192. data/lib/rdoc/version.rb +8 -0
  193. data/lib/rdoc.rb +201 -0
  194. data/man/ri.1 +247 -0
  195. data/rdoc.gemspec +249 -0
  196. metadata +279 -0
@@ -0,0 +1,2327 @@
1
+ # frozen_string_literal: true
2
+ ##
3
+ # This file contains stuff stolen outright from:
4
+ #
5
+ # rtags.rb -
6
+ # ruby-lex.rb - ruby lexcal analyzer
7
+ # ruby-token.rb - ruby tokens
8
+ # by Keiju ISHITSUKA (Nippon Rational Inc.)
9
+ #
10
+
11
+ ##
12
+ # Extracts code elements from a source file returning a TopLevel object
13
+ # containing the constituent file elements.
14
+ #
15
+ # This file is based on rtags
16
+ #
17
+ # RubyParser understands how to document:
18
+ # * classes
19
+ # * modules
20
+ # * methods
21
+ # * constants
22
+ # * aliases
23
+ # * private, public, protected
24
+ # * private_class_function, public_class_function
25
+ # * private_constant, public_constant
26
+ # * module_function
27
+ # * attr, attr_reader, attr_writer, attr_accessor
28
+ # * extra accessors given on the command line
29
+ # * metaprogrammed methods
30
+ # * require
31
+ # * include
32
+ #
33
+ # == Method Arguments
34
+ #
35
+ #--
36
+ # NOTE: I don't think this works, needs tests, remove the paragraph following
37
+ # this block when known to work
38
+ #
39
+ # The parser extracts the arguments from the method definition. You can
40
+ # override this with a custom argument definition using the :args: directive:
41
+ #
42
+ # ##
43
+ # # This method tries over and over until it is tired
44
+ #
45
+ # def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try
46
+ # puts thing_to_try
47
+ # go_go_go thing_to_try, tries - 1
48
+ # end
49
+ #
50
+ # If you have a more-complex set of overrides you can use the :call-seq:
51
+ # directive:
52
+ #++
53
+ #
54
+ # The parser extracts the arguments from the method definition. You can
55
+ # override this with a custom argument definition using the :call-seq:
56
+ # directive:
57
+ #
58
+ # ##
59
+ # # This method can be called with a range or an offset and length
60
+ # #
61
+ # # :call-seq:
62
+ # # my_method(Range)
63
+ # # my_method(offset, length)
64
+ #
65
+ # def my_method(*args)
66
+ # end
67
+ #
68
+ # The parser extracts +yield+ expressions from method bodies to gather the
69
+ # yielded argument names. If your method manually calls a block instead of
70
+ # yielding or you want to override the discovered argument names use
71
+ # the :yields: directive:
72
+ #
73
+ # ##
74
+ # # My method is awesome
75
+ #
76
+ # def my_method(&block) # :yields: happy, times
77
+ # block.call 1, 2
78
+ # end
79
+ #
80
+ # == Metaprogrammed Methods
81
+ #
82
+ # To pick up a metaprogrammed method, the parser looks for a comment starting
83
+ # with '##' before an identifier:
84
+ #
85
+ # ##
86
+ # # This is a meta-programmed method!
87
+ #
88
+ # add_my_method :meta_method, :arg1, :arg2
89
+ #
90
+ # The parser looks at the token after the identifier to determine the name, in
91
+ # this example, :meta_method. If a name cannot be found, a warning is printed
92
+ # and 'unknown is used.
93
+ #
94
+ # You can force the name of a method using the :method: directive:
95
+ #
96
+ # ##
97
+ # # :method: some_method!
98
+ #
99
+ # By default, meta-methods are instance methods. To indicate that a method is
100
+ # a singleton method instead use the :singleton-method: directive:
101
+ #
102
+ # ##
103
+ # # :singleton-method:
104
+ #
105
+ # You can also use the :singleton-method: directive with a name:
106
+ #
107
+ # ##
108
+ # # :singleton-method: some_method!
109
+ #
110
+ # You can define arguments for metaprogrammed methods via either the
111
+ # :call-seq:, :arg: or :args: directives.
112
+ #
113
+ # Additionally you can mark a method as an attribute by
114
+ # using :attr:, :attr_reader:, :attr_writer: or :attr_accessor:. Just like
115
+ # for :method:, the name is optional.
116
+ #
117
+ # ##
118
+ # # :attr_reader: my_attr_name
119
+ #
120
+ # == Hidden methods and attributes
121
+ #
122
+ # You can provide documentation for methods that don't appear using
123
+ # the :method:, :singleton-method: and :attr: directives:
124
+ #
125
+ # ##
126
+ # # :attr_writer: ghost_writer
127
+ # # There is an attribute here, but you can't see it!
128
+ #
129
+ # ##
130
+ # # :method: ghost_method
131
+ # # There is a method here, but you can't see it!
132
+ #
133
+ # ##
134
+ # # this is a comment for a regular method
135
+ #
136
+ # def regular_method() end
137
+ #
138
+ # Note that by default, the :method: directive will be ignored if there is a
139
+ # standard rdocable item following it.
140
+
141
+ require 'ripper'
142
+ require_relative 'ripper_state_lex'
143
+
144
+ class RDoc::Parser::Ruby < RDoc::Parser
145
+
146
+ parse_files_matching(/\.rbw?$/)
147
+
148
+ include RDoc::TokenStream
149
+ include RDoc::Parser::RubyTools
150
+
151
+ ##
152
+ # RDoc::NormalClass type
153
+
154
+ NORMAL = "::"
155
+
156
+ ##
157
+ # RDoc::SingleClass type
158
+
159
+ SINGLE = "<<"
160
+
161
+ ##
162
+ # Creates a new Ruby parser.
163
+
164
+ def initialize(top_level, file_name, content, options, stats)
165
+ super
166
+
167
+ if /\t/ =~ content then
168
+ tab_width = @options.tab_width
169
+ content = content.split(/\n/).map do |line|
170
+ 1 while line.gsub!(/\t+/) {
171
+ ' ' * (tab_width*$&.length - $`.length % tab_width)
172
+ } && $~
173
+ line
174
+ end.join("\n")
175
+ end
176
+
177
+ @size = 0
178
+ @token_listeners = nil
179
+ content = RDoc::Encoding.remove_magic_comment content
180
+ @scanner = RDoc::Parser::RipperStateLex.parse(content)
181
+ @content = content
182
+ @scanner_point = 0
183
+ @prev_seek = nil
184
+ @markup = @options.markup
185
+ @track_visibility = :nodoc != @options.visibility
186
+ @encoding = @options.encoding
187
+
188
+ reset
189
+ end
190
+
191
+ def tk_nl?(tk)
192
+ :on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
193
+ end
194
+
195
+ ##
196
+ # Retrieves the read token stream and replaces +pattern+ with +replacement+
197
+ # using gsub. If the result is only a ";" returns an empty string.
198
+
199
+ def get_tkread_clean pattern, replacement # :nodoc:
200
+ read = get_tkread.gsub(pattern, replacement).strip
201
+ return '' if read == ';'
202
+ read
203
+ end
204
+
205
+ ##
206
+ # Extracts the visibility information for the visibility token +tk+
207
+ # and +single+ class type identifier.
208
+ #
209
+ # Returns the visibility type (a string), the visibility (a symbol) and
210
+ # +singleton+ if the methods following should be converted to singleton
211
+ # methods.
212
+
213
+ def get_visibility_information tk, single # :nodoc:
214
+ vis_type = tk[:text]
215
+ singleton = single == SINGLE
216
+
217
+ vis =
218
+ case vis_type
219
+ when 'private' then :private
220
+ when 'protected' then :protected
221
+ when 'public' then :public
222
+ when 'private_class_method' then
223
+ singleton = true
224
+ :private
225
+ when 'public_class_method' then
226
+ singleton = true
227
+ :public
228
+ when 'module_function' then
229
+ singleton = true
230
+ :public
231
+ else
232
+ raise RDoc::Error, "Invalid visibility: #{tk.name}"
233
+ end
234
+
235
+ return vis_type, vis, singleton
236
+ end
237
+
238
+ ##
239
+ # Look for the first comment in a file that isn't a shebang line.
240
+
241
+ def collect_first_comment
242
+ skip_tkspace
243
+ comment = ''.dup
244
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
245
+ first_line = true
246
+ first_comment_tk_kind = nil
247
+ line_no = nil
248
+
249
+ tk = get_tk
250
+
251
+ while tk && (:on_comment == tk[:kind] or :on_embdoc == tk[:kind])
252
+ comment_body = retrieve_comment_body(tk)
253
+ if first_line and comment_body =~ /\A#!/ then
254
+ skip_tkspace
255
+ tk = get_tk
256
+ elsif first_line and comment_body =~ /\A#\s*-\*-/ then
257
+ first_line = false
258
+ skip_tkspace
259
+ tk = get_tk
260
+ else
261
+ break if first_comment_tk_kind and not first_comment_tk_kind === tk[:kind]
262
+ first_comment_tk_kind = tk[:kind]
263
+
264
+ line_no = tk[:line_no] if first_line
265
+ first_line = false
266
+ comment << comment_body
267
+ tk = get_tk
268
+
269
+ if :on_nl === tk then
270
+ skip_tkspace_without_nl
271
+ tk = get_tk
272
+ end
273
+ end
274
+ end
275
+
276
+ unget_tk tk
277
+
278
+ new_comment comment, line_no
279
+ end
280
+
281
+ ##
282
+ # Consumes trailing whitespace from the token stream
283
+
284
+ def consume_trailing_spaces # :nodoc:
285
+ skip_tkspace_without_nl
286
+ end
287
+
288
+ ##
289
+ # Creates a new attribute in +container+ with +name+.
290
+
291
+ def create_attr container, single, name, rw, comment # :nodoc:
292
+ att = RDoc::Attr.new get_tkread, name, rw, comment, single == SINGLE
293
+ record_location att
294
+
295
+ container.add_attribute att
296
+ @stats.add_attribute att
297
+
298
+ att
299
+ end
300
+
301
+ ##
302
+ # Creates a module alias in +container+ at +rhs_name+ (or at the top-level
303
+ # for "::") with the name from +constant+.
304
+
305
+ def create_module_alias container, constant, rhs_name # :nodoc:
306
+ mod = if rhs_name =~ /^::/ then
307
+ @store.find_class_or_module rhs_name
308
+ else
309
+ container.find_module_named rhs_name
310
+ end
311
+
312
+ container.add_module_alias mod, rhs_name, constant, @top_level
313
+ end
314
+
315
+ ##
316
+ # Aborts with +msg+
317
+
318
+ def error(msg)
319
+ msg = make_message msg
320
+
321
+ abort msg
322
+ end
323
+
324
+ ##
325
+ # Looks for a true or false token.
326
+
327
+ def get_bool
328
+ skip_tkspace
329
+ tk = get_tk
330
+ if :on_kw == tk[:kind] && 'true' == tk[:text]
331
+ true
332
+ elsif :on_kw == tk[:kind] && ('false' == tk[:text] || 'nil' == tk[:text])
333
+ false
334
+ else
335
+ unget_tk tk
336
+ true
337
+ end
338
+ end
339
+
340
+ ##
341
+ # Look for the name of a class of module (optionally with a leading :: or
342
+ # with :: separated named) and return the ultimate name, the associated
343
+ # container, and the given name (with the ::).
344
+
345
+ def get_class_or_module container, ignore_constants = false
346
+ skip_tkspace
347
+ name_t = get_tk
348
+ given_name = ''.dup
349
+
350
+ # class ::A -> A is in the top level
351
+ if :on_op == name_t[:kind] and '::' == name_t[:text] then # bug
352
+ name_t = get_tk
353
+ container = @top_level
354
+ given_name << '::'
355
+ end
356
+
357
+ skip_tkspace_without_nl
358
+ given_name << name_t[:text]
359
+
360
+ is_self = name_t[:kind] == :on_op && name_t[:text] == '<<'
361
+ new_modules = []
362
+ while !is_self && (tk = peek_tk) and :on_op == tk[:kind] and '::' == tk[:text] do
363
+ prev_container = container
364
+ container = container.find_module_named name_t[:text]
365
+ container ||=
366
+ if ignore_constants then
367
+ c = RDoc::NormalModule.new name_t[:text]
368
+ c.store = @store
369
+ new_modules << [prev_container, c]
370
+ c
371
+ else
372
+ c = prev_container.add_module RDoc::NormalModule, name_t[:text]
373
+ c.ignore unless prev_container.document_children
374
+ @top_level.add_to_classes_or_modules c
375
+ c
376
+ end
377
+
378
+ record_location container
379
+
380
+ get_tk
381
+ skip_tkspace
382
+ if :on_lparen == peek_tk[:kind] # ProcObjectInConstant::()
383
+ parse_method_or_yield_parameters
384
+ break
385
+ end
386
+ name_t = get_tk
387
+ unless :on_const == name_t[:kind] || :on_ident == name_t[:kind]
388
+ raise RDoc::Error, "Invalid class or module definition: #{given_name}"
389
+ end
390
+ if prev_container == container and !ignore_constants
391
+ given_name = name_t[:text]
392
+ else
393
+ given_name << '::' + name_t[:text]
394
+ end
395
+ end
396
+
397
+ skip_tkspace_without_nl
398
+
399
+ return [container, name_t, given_name, new_modules]
400
+ end
401
+
402
+ ##
403
+ # Return a superclass, which can be either a constant of an expression
404
+
405
+ def get_class_specification
406
+ tk = peek_tk
407
+ if tk.nil?
408
+ return ''
409
+ elsif :on_kw == tk[:kind] && 'self' == tk[:text]
410
+ return 'self'
411
+ elsif :on_gvar == tk[:kind]
412
+ return ''
413
+ end
414
+
415
+ res = get_constant
416
+
417
+ skip_tkspace_without_nl
418
+
419
+ get_tkread # empty out read buffer
420
+
421
+ tk = get_tk
422
+ return res unless tk
423
+
424
+ case tk[:kind]
425
+ when :on_nl, :on_comment, :on_embdoc, :on_semicolon then
426
+ unget_tk(tk)
427
+ return res
428
+ end
429
+
430
+ res += parse_call_parameters(tk)
431
+ res
432
+ end
433
+
434
+ ##
435
+ # Parse a constant, which might be qualified by one or more class or module
436
+ # names
437
+
438
+ def get_constant
439
+ res = ""
440
+ skip_tkspace_without_nl
441
+ tk = get_tk
442
+
443
+ while tk && ((:on_op == tk[:kind] && '::' == tk[:text]) || :on_const == tk[:kind]) do
444
+ res += tk[:text]
445
+ tk = get_tk
446
+ end
447
+
448
+ unget_tk(tk)
449
+ res
450
+ end
451
+
452
+ ##
453
+ # Get an included module that may be surrounded by parens
454
+
455
+ def get_included_module_with_optional_parens
456
+ skip_tkspace_without_nl
457
+ get_tkread
458
+ tk = get_tk
459
+ end_token = get_end_token tk
460
+ return '' unless end_token
461
+
462
+ nest = 0
463
+ continue = false
464
+ only_constant = true
465
+
466
+ while tk != nil do
467
+ is_element_of_constant = false
468
+ case tk[:kind]
469
+ when :on_semicolon then
470
+ break if nest == 0
471
+ when :on_lbracket then
472
+ nest += 1
473
+ when :on_rbracket then
474
+ nest -= 1
475
+ when :on_lbrace then
476
+ nest += 1
477
+ when :on_rbrace then
478
+ nest -= 1
479
+ if nest <= 0
480
+ # we might have a.each { |i| yield i }
481
+ unget_tk(tk) if nest < 0
482
+ break
483
+ end
484
+ when :on_lparen then
485
+ nest += 1
486
+ when end_token[:kind] then
487
+ if end_token[:kind] == :on_rparen
488
+ nest -= 1
489
+ break if nest <= 0
490
+ else
491
+ break if nest <= 0
492
+ end
493
+ when :on_rparen then
494
+ nest -= 1
495
+ when :on_comment, :on_embdoc then
496
+ @read.pop
497
+ if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
498
+ (!continue or (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0) then
499
+ break if !continue and nest <= 0
500
+ end
501
+ when :on_comma then
502
+ continue = true
503
+ when :on_ident then
504
+ continue = false if continue
505
+ when :on_kw then
506
+ case tk[:text]
507
+ when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
508
+ nest += 1
509
+ when 'if', 'unless', 'while', 'until', 'rescue'
510
+ # postfix if/unless/while/until/rescue must be EXPR_LABEL
511
+ nest += 1 unless (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0
512
+ when 'end'
513
+ nest -= 1
514
+ break if nest == 0
515
+ end
516
+ when :on_const then
517
+ is_element_of_constant = true
518
+ when :on_op then
519
+ is_element_of_constant = true if '::' == tk[:text]
520
+ end
521
+ only_constant = false unless is_element_of_constant
522
+ tk = get_tk
523
+ end
524
+
525
+ if only_constant
526
+ get_tkread_clean(/\s+/, ' ')
527
+ else
528
+ ''
529
+ end
530
+ end
531
+
532
+ ##
533
+ # Little hack going on here. In the statement:
534
+ #
535
+ # f = 2*(1+yield)
536
+ #
537
+ # We see the RPAREN as the next token, so we need to exit early. This still
538
+ # won't catch all cases (such as "a = yield + 1"
539
+
540
+ def get_end_token tk # :nodoc:
541
+ case tk[:kind]
542
+ when :on_lparen
543
+ token = RDoc::Parser::RipperStateLex::Token.new
544
+ token[:kind] = :on_rparen
545
+ token[:text] = ')'
546
+ token
547
+ when :on_rparen
548
+ nil
549
+ else
550
+ token = RDoc::Parser::RipperStateLex::Token.new
551
+ token[:kind] = :on_nl
552
+ token[:text] = "\n"
553
+ token
554
+ end
555
+ end
556
+
557
+ ##
558
+ # Retrieves the method container for a singleton method.
559
+
560
+ def get_method_container container, name_t # :nodoc:
561
+ prev_container = container
562
+ container = container.find_module_named(name_t[:text])
563
+
564
+ unless container then
565
+ constant = prev_container.constants.find do |const|
566
+ const.name == name_t[:text]
567
+ end
568
+
569
+ if constant then
570
+ parse_method_dummy prev_container
571
+ return
572
+ end
573
+ end
574
+
575
+ unless container then
576
+ # TODO seems broken, should starting at Object in @store
577
+ obj = name_t[:text].split("::").inject(Object) do |state, item|
578
+ state.const_get(item)
579
+ end rescue nil
580
+
581
+ type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule
582
+
583
+ unless [Class, Module].include?(obj.class) then
584
+ warn("Couldn't find #{name_t[:text]}. Assuming it's a module")
585
+ end
586
+
587
+ if type == RDoc::NormalClass then
588
+ sclass = obj.superclass ? obj.superclass.name : nil
589
+ container = prev_container.add_class type, name_t[:text], sclass
590
+ else
591
+ container = prev_container.add_module type, name_t[:text]
592
+ end
593
+
594
+ record_location container
595
+ end
596
+
597
+ container
598
+ end
599
+
600
+ ##
601
+ # Extracts a name or symbol from the token stream.
602
+
603
+ def get_symbol_or_name
604
+ tk = get_tk
605
+ case tk[:kind]
606
+ when :on_symbol then
607
+ text = tk[:text].sub(/^:/, '')
608
+
609
+ next_tk = peek_tk
610
+ if next_tk && :on_op == next_tk[:kind] && '=' == next_tk[:text] then
611
+ get_tk
612
+ text << '='
613
+ end
614
+
615
+ text
616
+ when :on_ident, :on_const, :on_gvar, :on_cvar, :on_ivar, :on_op, :on_kw then
617
+ tk[:text]
618
+ when :on_tstring, :on_dstring then
619
+ tk[:text][1..-2]
620
+ else
621
+ raise RDoc::Error, "Name or symbol expected (got #{tk})"
622
+ end
623
+ end
624
+
625
+ ##
626
+ # Marks containers between +container+ and +ancestor+ as ignored
627
+
628
+ def suppress_parents container, ancestor # :nodoc:
629
+ while container and container != ancestor do
630
+ container.suppress unless container.documented?
631
+ container = container.parent
632
+ end
633
+ end
634
+
635
+ ##
636
+ # Look for directives in a normal comment block:
637
+ #
638
+ # # :stopdoc:
639
+ # # Don't display comment from this point forward
640
+ #
641
+ # This routine modifies its +comment+ parameter.
642
+
643
+ def look_for_directives_in container, comment
644
+ @preprocess.handle comment, container do |directive, param|
645
+ case directive
646
+ when 'method', 'singleton-method',
647
+ 'attr', 'attr_accessor', 'attr_reader', 'attr_writer' then
648
+ false # handled elsewhere
649
+ when 'section' then
650
+ break unless container.kind_of?(RDoc::Context)
651
+ container.set_current_section param, comment.dup
652
+ comment.text = ''
653
+ break
654
+ end
655
+ end
656
+
657
+ comment.remove_private
658
+ end
659
+
660
+ ##
661
+ # Adds useful info about the parser to +message+
662
+
663
+ def make_message message
664
+ prefix = "#{@file_name}:".dup
665
+
666
+ tk = peek_tk
667
+ prefix << "#{tk[:line_no]}:#{tk[:char_no]}:" if tk
668
+
669
+ "#{prefix} #{message}"
670
+ end
671
+
672
+ ##
673
+ # Creates a comment with the correct format
674
+
675
+ def new_comment comment, line_no = nil
676
+ c = RDoc::Comment.new comment, @top_level, :ruby
677
+ c.line = line_no
678
+ c.format = @markup
679
+ c
680
+ end
681
+
682
+ ##
683
+ # Creates an RDoc::Attr for the name following +tk+, setting the comment to
684
+ # +comment+.
685
+
686
+ def parse_attr(context, single, tk, comment)
687
+ line_no = tk[:line_no]
688
+
689
+ args = parse_symbol_arg 1
690
+ if args.size > 0 then
691
+ name = args[0]
692
+ rw = "R"
693
+ skip_tkspace_without_nl
694
+ tk = get_tk
695
+
696
+ if :on_comma == tk[:kind] then
697
+ rw = "RW" if get_bool
698
+ else
699
+ unget_tk tk
700
+ end
701
+
702
+ att = create_attr context, single, name, rw, comment
703
+ att.line = line_no
704
+
705
+ read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
706
+ else
707
+ warn "'attr' ignored - looks like a variable"
708
+ end
709
+ end
710
+
711
+ ##
712
+ # Creates an RDoc::Attr for each attribute listed after +tk+, setting the
713
+ # comment for each to +comment+.
714
+
715
+ def parse_attr_accessor(context, single, tk, comment)
716
+ line_no = tk[:line_no]
717
+
718
+ args = parse_symbol_arg
719
+ rw = "?"
720
+
721
+ tmp = RDoc::CodeObject.new
722
+ read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
723
+ # TODO In most other places we let the context keep track of document_self
724
+ # and add found items appropriately but here we do not. I'm not sure why.
725
+ return if @track_visibility and not tmp.document_self
726
+
727
+ case tk[:text]
728
+ when "attr_reader" then rw = "R"
729
+ when "attr_writer" then rw = "W"
730
+ when "attr_accessor" then rw = "RW"
731
+ else
732
+ rw = '?'
733
+ end
734
+
735
+ for name in args
736
+ att = create_attr context, single, name, rw, comment
737
+ att.line = line_no
738
+ end
739
+ end
740
+
741
+ ##
742
+ # Parses an +alias+ in +context+ with +comment+
743
+
744
+ def parse_alias(context, single, tk, comment)
745
+ line_no = tk[:line_no]
746
+
747
+ skip_tkspace
748
+
749
+ if :on_lparen === peek_tk[:kind] then
750
+ get_tk
751
+ skip_tkspace
752
+ end
753
+
754
+ new_name = get_symbol_or_name
755
+
756
+ skip_tkspace
757
+ if :on_comma === peek_tk[:kind] then
758
+ get_tk
759
+ skip_tkspace
760
+ end
761
+
762
+ begin
763
+ old_name = get_symbol_or_name
764
+ rescue RDoc::Error
765
+ return
766
+ end
767
+
768
+ al = RDoc::Alias.new(get_tkread, old_name, new_name, comment,
769
+ single == SINGLE)
770
+ record_location al
771
+ al.line = line_no
772
+
773
+ read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
774
+ context.add_alias al
775
+ @stats.add_alias al
776
+
777
+ al
778
+ end
779
+
780
+ ##
781
+ # Extracts call parameters from the token stream.
782
+
783
+ def parse_call_parameters(tk)
784
+ end_token = case tk[:kind]
785
+ when :on_lparen
786
+ :on_rparen
787
+ when :on_rparen
788
+ return ""
789
+ else
790
+ :on_nl
791
+ end
792
+ nest = 0
793
+
794
+ loop do
795
+ break if tk.nil?
796
+ case tk[:kind]
797
+ when :on_semicolon
798
+ break
799
+ when :on_lparen
800
+ nest += 1
801
+ when end_token
802
+ if end_token == :on_rparen
803
+ nest -= 1
804
+ break if RDoc::Parser::RipperStateLex.end?(tk) and nest <= 0
805
+ else
806
+ break if RDoc::Parser::RipperStateLex.end?(tk)
807
+ end
808
+ when :on_comment, :on_embdoc
809
+ unget_tk(tk)
810
+ break
811
+ when :on_op
812
+ if tk[:text] =~ /^(.{1,2})?=$/
813
+ unget_tk(tk)
814
+ break
815
+ end
816
+ end
817
+ tk = get_tk
818
+ end
819
+
820
+ get_tkread_clean "\n", " "
821
+ end
822
+
823
+ ##
824
+ # Parses a class in +context+ with +comment+
825
+
826
+ def parse_class container, single, tk, comment
827
+ line_no = tk[:line_no]
828
+
829
+ declaration_context = container
830
+ container, name_t, given_name, = get_class_or_module container
831
+
832
+ if name_t[:kind] == :on_const
833
+ cls = parse_class_regular container, declaration_context, single,
834
+ name_t, given_name, comment
835
+ elsif name_t[:kind] == :on_op && name_t[:text] == '<<'
836
+ case name = get_class_specification
837
+ when 'self', container.name
838
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
839
+ parse_statements container, SINGLE
840
+ return # don't update line
841
+ else
842
+ cls = parse_class_singleton container, name, comment
843
+ end
844
+ else
845
+ warn "Expected class name or '<<'. Got #{name_t[:kind]}: #{name_t[:text].inspect}"
846
+ return
847
+ end
848
+
849
+ cls.line = line_no
850
+
851
+ # after end modifiers
852
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
853
+
854
+ cls
855
+ end
856
+
857
+ ##
858
+ # Parses and creates a regular class
859
+
860
+ def parse_class_regular container, declaration_context, single, # :nodoc:
861
+ name_t, given_name, comment
862
+ superclass = '::Object'
863
+
864
+ if given_name =~ /^::/ then
865
+ declaration_context = @top_level
866
+ given_name = $'
867
+ end
868
+
869
+ tk = peek_tk
870
+ if tk[:kind] == :on_op && tk[:text] == '<' then
871
+ get_tk
872
+ skip_tkspace
873
+ superclass = get_class_specification
874
+ superclass = '(unknown)' if superclass.empty?
875
+ end
876
+
877
+ cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass
878
+ cls = declaration_context.add_class cls_type, given_name, superclass
879
+ cls.ignore unless container.document_children
880
+
881
+ read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
882
+ record_location cls
883
+
884
+ cls.add_comment comment, @top_level
885
+
886
+ @top_level.add_to_classes_or_modules cls
887
+ @stats.add_class cls
888
+
889
+ suppress_parents container, declaration_context unless cls.document_self
890
+
891
+ parse_statements cls
892
+
893
+ cls
894
+ end
895
+
896
+ ##
897
+ # Parses a singleton class in +container+ with the given +name+ and
898
+ # +comment+.
899
+
900
+ def parse_class_singleton container, name, comment # :nodoc:
901
+ other = @store.find_class_named name
902
+
903
+ unless other then
904
+ if name =~ /^::/ then
905
+ name = $'
906
+ container = @top_level
907
+ end
908
+
909
+ other = container.add_module RDoc::NormalModule, name
910
+ record_location other
911
+
912
+ # class << $gvar
913
+ other.ignore if name.empty?
914
+
915
+ other.add_comment comment, @top_level
916
+ end
917
+
918
+ # notify :nodoc: all if not a constant-named class/module
919
+ # (and remove any comment)
920
+ unless name =~ /\A(::)?[A-Z]/ then
921
+ other.document_self = nil
922
+ other.document_children = false
923
+ other.clear_comment
924
+ end
925
+
926
+ @top_level.add_to_classes_or_modules other
927
+ @stats.add_class other
928
+
929
+ read_documentation_modifiers other, RDoc::CLASS_MODIFIERS
930
+ parse_statements(other, SINGLE)
931
+
932
+ other
933
+ end
934
+
935
+ ##
936
+ # Parses a constant in +context+ with +comment+. If +ignore_constants+ is
937
+ # true, no found constants will be added to RDoc.
938
+
939
+ def parse_constant container, tk, comment, ignore_constants = false
940
+ line_no = tk[:line_no]
941
+
942
+ name = tk[:text]
943
+ skip_tkspace_without_nl
944
+
945
+ return unless name =~ /^\w+$/
946
+
947
+ new_modules = []
948
+ if :on_op == peek_tk[:kind] && '::' == peek_tk[:text] then
949
+ unget_tk tk
950
+
951
+ container, name_t, _, new_modules = get_class_or_module container, true
952
+
953
+ name = name_t[:text]
954
+ end
955
+
956
+ is_array_or_hash = false
957
+ if peek_tk && :on_lbracket == peek_tk[:kind]
958
+ get_tk
959
+ nest = 1
960
+ while bracket_tk = get_tk
961
+ case bracket_tk[:kind]
962
+ when :on_lbracket
963
+ nest += 1
964
+ when :on_rbracket
965
+ nest -= 1
966
+ break if nest == 0
967
+ end
968
+ end
969
+ skip_tkspace_without_nl
970
+ is_array_or_hash = true
971
+ end
972
+
973
+ unless peek_tk && :on_op == peek_tk[:kind] && '=' == peek_tk[:text] then
974
+ return false
975
+ end
976
+ get_tk
977
+
978
+ unless ignore_constants
979
+ new_modules.each do |prev_c, new_module|
980
+ prev_c.add_module_by_normal_module new_module
981
+ new_module.ignore unless prev_c.document_children
982
+ @top_level.add_to_classes_or_modules new_module
983
+ end
984
+ end
985
+
986
+ value = ''
987
+ con = RDoc::Constant.new name, value, comment
988
+
989
+ body = parse_constant_body container, con, is_array_or_hash
990
+
991
+ return unless body
992
+
993
+ con.value = body
994
+ record_location con
995
+ con.line = line_no
996
+ read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS
997
+
998
+ return if is_array_or_hash
999
+
1000
+ @stats.add_constant con
1001
+ container.add_constant con
1002
+
1003
+ true
1004
+ end
1005
+
1006
+ def parse_constant_body container, constant, is_array_or_hash # :nodoc:
1007
+ nest = 0
1008
+ rhs_name = ''.dup
1009
+
1010
+ get_tkread
1011
+
1012
+ tk = get_tk
1013
+
1014
+ body = nil
1015
+ loop do
1016
+ break if tk.nil?
1017
+ if :on_semicolon == tk[:kind] then
1018
+ break if nest <= 0
1019
+ elsif [:on_tlambeg, :on_lparen, :on_lbrace, :on_lbracket].include?(tk[:kind]) then
1020
+ nest += 1
1021
+ elsif (:on_kw == tk[:kind] && 'def' == tk[:text]) then
1022
+ nest += 1
1023
+ elsif (:on_kw == tk[:kind] && %w{do if unless case begin}.include?(tk[:text])) then
1024
+ if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0
1025
+ nest += 1
1026
+ end
1027
+ elsif [:on_rparen, :on_rbrace, :on_rbracket].include?(tk[:kind]) ||
1028
+ (:on_kw == tk[:kind] && 'end' == tk[:text]) then
1029
+ nest -= 1
1030
+ elsif (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) then
1031
+ unget_tk tk
1032
+ if nest <= 0 and RDoc::Parser::RipperStateLex.end?(tk) then
1033
+ body = get_tkread_clean(/^[ \t]+/, '')
1034
+ read_documentation_modifiers constant, RDoc::CONSTANT_MODIFIERS
1035
+ break
1036
+ else
1037
+ read_documentation_modifiers constant, RDoc::CONSTANT_MODIFIERS
1038
+ end
1039
+ elsif :on_const == tk[:kind] then
1040
+ rhs_name << tk[:text]
1041
+
1042
+ next_tk = peek_tk
1043
+ if nest <= 0 and (next_tk.nil? || :on_nl == next_tk[:kind]) then
1044
+ create_module_alias container, constant, rhs_name unless is_array_or_hash
1045
+ break
1046
+ end
1047
+ elsif :on_nl == tk[:kind] then
1048
+ if nest <= 0 and RDoc::Parser::RipperStateLex.end?(tk) then
1049
+ unget_tk tk
1050
+ break
1051
+ end
1052
+ elsif :on_op == tk[:kind] && '::' == tk[:text]
1053
+ rhs_name << '::'
1054
+ end
1055
+ tk = get_tk
1056
+ end
1057
+
1058
+ body ? body : get_tkread_clean(/^[ \t]+/, '')
1059
+ end
1060
+
1061
+ ##
1062
+ # Generates an RDoc::Method or RDoc::Attr from +comment+ by looking for
1063
+ # :method: or :attr: directives in +comment+.
1064
+
1065
+ def parse_comment container, tk, comment
1066
+ return parse_comment_tomdoc container, tk, comment if @markup == 'tomdoc'
1067
+ column = tk[:char_no]
1068
+ line_no = comment.line.nil? ? tk[:line_no] : comment.line
1069
+
1070
+ comment.text = comment.text.sub(/(^# +:?)(singleton-)(method:)/, '\1\3')
1071
+ singleton = !!$~
1072
+
1073
+ co =
1074
+ if (comment.text = comment.text.sub(/^# +:?method: *(\S*).*?\n/i, '')) && !!$~ then
1075
+ line_no += $`.count("\n")
1076
+ parse_comment_ghost container, comment.text, $1, column, line_no, comment
1077
+ elsif (comment.text = comment.text.sub(/# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '')) && !!$~ then
1078
+ parse_comment_attr container, $1, $3, comment
1079
+ end
1080
+
1081
+ if co then
1082
+ co.singleton = singleton
1083
+ co.line = line_no
1084
+ end
1085
+
1086
+ true
1087
+ end
1088
+
1089
+ ##
1090
+ # Parse a comment that is describing an attribute in +container+ with the
1091
+ # given +name+ and +comment+.
1092
+
1093
+ def parse_comment_attr container, type, name, comment # :nodoc:
1094
+ return if name.empty?
1095
+
1096
+ rw = case type
1097
+ when 'attr_reader' then 'R'
1098
+ when 'attr_writer' then 'W'
1099
+ else 'RW'
1100
+ end
1101
+
1102
+ create_attr container, NORMAL, name, rw, comment
1103
+ end
1104
+
1105
+ def parse_comment_ghost container, text, name, column, line_no, # :nodoc:
1106
+ comment
1107
+ name = nil if name.empty?
1108
+
1109
+ meth = RDoc::GhostMethod.new get_tkread, name
1110
+ record_location meth
1111
+
1112
+ meth.start_collecting_tokens
1113
+ indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
1114
+ position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
1115
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
1116
+ newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
1117
+ meth.add_tokens [position_comment, newline, indent]
1118
+
1119
+ meth.params =
1120
+ if text.sub!(/^#\s+:?args?:\s*(.*?)\s*$/i, '') then
1121
+ $1
1122
+ else
1123
+ ''
1124
+ end
1125
+
1126
+ comment.normalize
1127
+ comment.extract_call_seq meth
1128
+
1129
+ return unless meth.name
1130
+
1131
+ container.add_method meth
1132
+
1133
+ meth.comment = comment
1134
+
1135
+ @stats.add_method meth
1136
+
1137
+ meth
1138
+ end
1139
+
1140
+ ##
1141
+ # Creates an RDoc::Method on +container+ from +comment+ if there is a
1142
+ # Signature section in the comment
1143
+
1144
+ def parse_comment_tomdoc container, tk, comment
1145
+ return unless signature = RDoc::TomDoc.signature(comment)
1146
+ column = tk[:char_no]
1147
+ line_no = tk[:line_no]
1148
+
1149
+ name, = signature.split %r%[ \(]%, 2
1150
+
1151
+ meth = RDoc::GhostMethod.new get_tkread, name
1152
+ record_location meth
1153
+ meth.line = line_no
1154
+
1155
+ meth.start_collecting_tokens
1156
+ indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
1157
+ position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
1158
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
1159
+ newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
1160
+ meth.add_tokens [position_comment, newline, indent]
1161
+
1162
+ meth.call_seq = signature
1163
+
1164
+ comment.normalize
1165
+
1166
+ return unless meth.name
1167
+
1168
+ container.add_method meth
1169
+
1170
+ meth.comment = comment
1171
+
1172
+ @stats.add_method meth
1173
+ end
1174
+
1175
+ ##
1176
+ # Parses an +include+ or +extend+, indicated by the +klass+ and adds it to
1177
+ # +container+ # with +comment+
1178
+
1179
+ def parse_extend_or_include klass, container, comment # :nodoc:
1180
+ loop do
1181
+ skip_tkspace_comment
1182
+
1183
+ name = get_included_module_with_optional_parens
1184
+
1185
+ unless name.empty? then
1186
+ obj = container.add klass, name, comment
1187
+ record_location obj
1188
+ end
1189
+
1190
+ return if peek_tk.nil? || :on_comma != peek_tk[:kind]
1191
+
1192
+ get_tk
1193
+ end
1194
+ end
1195
+
1196
+ ##
1197
+ # Parses identifiers that can create new methods or change visibility.
1198
+ #
1199
+ # Returns true if the comment was not consumed.
1200
+
1201
+ def parse_identifier container, single, tk, comment # :nodoc:
1202
+ case tk[:text]
1203
+ when 'private', 'protected', 'public', 'private_class_method',
1204
+ 'public_class_method', 'module_function' then
1205
+ parse_visibility container, single, tk
1206
+ return true
1207
+ when 'private_constant', 'public_constant'
1208
+ parse_constant_visibility container, single, tk
1209
+ return true
1210
+ when 'attr' then
1211
+ parse_attr container, single, tk, comment
1212
+ when /^attr_(reader|writer|accessor)$/ then
1213
+ parse_attr_accessor container, single, tk, comment
1214
+ when 'alias_method' then
1215
+ parse_alias container, single, tk, comment
1216
+ when 'require', 'include' then
1217
+ # ignore
1218
+ else
1219
+ if comment.text =~ /\A#\#$/ then
1220
+ case comment.text
1221
+ when /^# +:?attr(_reader|_writer|_accessor)?:/ then
1222
+ parse_meta_attr container, single, tk, comment
1223
+ else
1224
+ method = parse_meta_method container, single, tk, comment
1225
+ method.params = container.params if
1226
+ container.params
1227
+ method.block_params = container.block_params if
1228
+ container.block_params
1229
+ end
1230
+ end
1231
+ end
1232
+
1233
+ false
1234
+ end
1235
+
1236
+ ##
1237
+ # Parses a meta-programmed attribute and creates an RDoc::Attr.
1238
+ #
1239
+ # To create foo and bar attributes on class C with comment "My attributes":
1240
+ #
1241
+ # class C
1242
+ #
1243
+ # ##
1244
+ # # :attr:
1245
+ # #
1246
+ # # My attributes
1247
+ #
1248
+ # my_attr :foo, :bar
1249
+ #
1250
+ # end
1251
+ #
1252
+ # To create a foo attribute on class C with comment "My attribute":
1253
+ #
1254
+ # class C
1255
+ #
1256
+ # ##
1257
+ # # :attr: foo
1258
+ # #
1259
+ # # My attribute
1260
+ #
1261
+ # my_attr :foo, :bar
1262
+ #
1263
+ # end
1264
+
1265
+ def parse_meta_attr(context, single, tk, comment)
1266
+ args = parse_symbol_arg
1267
+ rw = "?"
1268
+
1269
+ # If nodoc is given, don't document any of them
1270
+
1271
+ tmp = RDoc::CodeObject.new
1272
+ read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
1273
+
1274
+ regexp = /^# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i
1275
+ if regexp =~ comment.text then
1276
+ comment.text = comment.text.sub(regexp, '')
1277
+ rw = case $1
1278
+ when 'attr_reader' then 'R'
1279
+ when 'attr_writer' then 'W'
1280
+ else 'RW'
1281
+ end
1282
+ name = $3 unless $3.empty?
1283
+ end
1284
+
1285
+ if name then
1286
+ att = create_attr context, single, name, rw, comment
1287
+ else
1288
+ args.each do |attr_name|
1289
+ att = create_attr context, single, attr_name, rw, comment
1290
+ end
1291
+ end
1292
+
1293
+ att
1294
+ end
1295
+
1296
+ ##
1297
+ # Parses a meta-programmed method
1298
+
1299
+ def parse_meta_method(container, single, tk, comment)
1300
+ column = tk[:char_no]
1301
+ line_no = tk[:line_no]
1302
+
1303
+ start_collecting_tokens
1304
+ add_token tk
1305
+ add_token_listener self
1306
+
1307
+ skip_tkspace_without_nl
1308
+
1309
+ comment.text = comment.text.sub(/(^# +:?)(singleton-)(method:)/, '\1\3')
1310
+ singleton = !!$~
1311
+
1312
+ name = parse_meta_method_name comment, tk
1313
+
1314
+ return unless name
1315
+
1316
+ meth = RDoc::MetaMethod.new get_tkread, name
1317
+ record_location meth
1318
+ meth.line = line_no
1319
+ meth.singleton = singleton
1320
+
1321
+ remove_token_listener self
1322
+
1323
+ meth.start_collecting_tokens
1324
+ indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
1325
+ position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
1326
+ position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
1327
+ newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
1328
+ meth.add_tokens [position_comment, newline, indent]
1329
+ meth.add_tokens @token_stream
1330
+
1331
+ parse_meta_method_params container, single, meth, tk, comment
1332
+
1333
+ meth.comment = comment
1334
+
1335
+ @stats.add_method meth
1336
+
1337
+ meth
1338
+ end
1339
+
1340
+ ##
1341
+ # Parses the name of a metaprogrammed method. +comment+ is used to
1342
+ # determine the name while +tk+ is used in an error message if the name
1343
+ # cannot be determined.
1344
+
1345
+ def parse_meta_method_name comment, tk # :nodoc:
1346
+ if comment.text.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
1347
+ return $1 unless $1.empty?
1348
+ end
1349
+
1350
+ name_t = get_tk
1351
+
1352
+ if :on_symbol == name_t[:kind] then
1353
+ name_t[:text][1..-1]
1354
+ elsif :on_tstring == name_t[:kind] then
1355
+ name_t[:text][1..-2]
1356
+ elsif :on_op == name_t[:kind] && '=' == name_t[:text] then # ignore
1357
+ remove_token_listener self
1358
+
1359
+ nil
1360
+ else
1361
+ warn "unknown name token #{name_t.inspect} for meta-method '#{tk[:text]}'"
1362
+ 'unknown'
1363
+ end
1364
+ end
1365
+
1366
+ ##
1367
+ # Parses the parameters and block for a meta-programmed method.
1368
+
1369
+ def parse_meta_method_params container, single, meth, tk, comment # :nodoc:
1370
+ token_listener meth do
1371
+ meth.params = ''
1372
+
1373
+ look_for_directives_in meth, comment
1374
+ comment.normalize
1375
+ comment.extract_call_seq meth
1376
+
1377
+ container.add_method meth
1378
+
1379
+ last_tk = tk
1380
+
1381
+ while tk = get_tk do
1382
+ if :on_semicolon == tk[:kind] then
1383
+ break
1384
+ elsif :on_nl == tk[:kind] then
1385
+ break unless last_tk and :on_comma == last_tk[:kind]
1386
+ elsif :on_sp == tk[:kind] then
1387
+ # expression continues
1388
+ elsif :on_kw == tk[:kind] && 'do' == tk[:text] then
1389
+ parse_statements container, single, meth
1390
+ break
1391
+ else
1392
+ last_tk = tk
1393
+ end
1394
+ end
1395
+ end
1396
+ end
1397
+
1398
+ ##
1399
+ # Parses a normal method defined by +def+
1400
+
1401
+ def parse_method(container, single, tk, comment)
1402
+ singleton = nil
1403
+ added_container = false
1404
+ name = nil
1405
+ column = tk[:char_no]
1406
+ line_no = tk[:line_no]
1407
+
1408
+ start_collecting_tokens
1409
+ add_token tk
1410
+
1411
+ token_listener self do
1412
+ prev_container = container
1413
+ name, container, singleton = parse_method_name container
1414
+ added_container = container != prev_container
1415
+ end
1416
+
1417
+ return unless name
1418
+
1419
+ meth = RDoc::AnyMethod.new get_tkread, name
1420
+ look_for_directives_in meth, comment
1421
+ meth.singleton = single == SINGLE ? true : singleton
1422
+
1423
+ record_location meth
1424
+ meth.line = line_no
1425
+
1426
+ meth.start_collecting_tokens
1427
+ indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
1428
+ token = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
1429
+ token[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
1430
+ newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
1431
+ meth.add_tokens [token, newline, indent]
1432
+ meth.add_tokens @token_stream
1433
+
1434
+ parse_method_params_and_body container, single, meth, added_container
1435
+
1436
+ comment.normalize
1437
+ comment.extract_call_seq meth
1438
+
1439
+ meth.comment = comment
1440
+
1441
+ # after end modifiers
1442
+ read_documentation_modifiers meth, RDoc::METHOD_MODIFIERS
1443
+
1444
+ @stats.add_method meth
1445
+ end
1446
+
1447
+ ##
1448
+ # Parses the parameters and body of +meth+
1449
+
1450
+ def parse_method_params_and_body container, single, meth, added_container
1451
+ token_listener meth do
1452
+ parse_method_parameters meth
1453
+
1454
+ if meth.document_self or not @track_visibility then
1455
+ container.add_method meth
1456
+ elsif added_container then
1457
+ container.document_self = false
1458
+ end
1459
+
1460
+ # Having now read the method parameters and documentation modifiers, we
1461
+ # now know whether we have to rename #initialize to ::new
1462
+
1463
+ if meth.name == "initialize" && !meth.singleton then
1464
+ if meth.dont_rename_initialize then
1465
+ meth.visibility = :protected
1466
+ else
1467
+ meth.singleton = true
1468
+ meth.name = "new"
1469
+ meth.visibility = :public
1470
+ end
1471
+ end
1472
+
1473
+ parse_statements container, single, meth
1474
+ end
1475
+ end
1476
+
1477
+ ##
1478
+ # Parses a method that needs to be ignored.
1479
+
1480
+ def parse_method_dummy container
1481
+ dummy = RDoc::Context.new
1482
+ dummy.parent = container
1483
+ dummy.store = container.store
1484
+ skip_method dummy
1485
+ end
1486
+
1487
+ ##
1488
+ # Parses the name of a method in +container+.
1489
+ #
1490
+ # Returns the method name, the container it is in (for def Foo.name) and if
1491
+ # it is a singleton or regular method.
1492
+
1493
+ def parse_method_name container # :nodoc:
1494
+ skip_tkspace
1495
+ name_t = get_tk
1496
+ back_tk = skip_tkspace_without_nl
1497
+ singleton = false
1498
+
1499
+ dot = get_tk
1500
+ if dot[:kind] == :on_period || (dot[:kind] == :on_op && dot[:text] == '::') then
1501
+ singleton = true
1502
+
1503
+ name, container = parse_method_name_singleton container, name_t
1504
+ else
1505
+ unget_tk dot
1506
+ back_tk.reverse_each do |token|
1507
+ unget_tk token
1508
+ end
1509
+
1510
+ name = parse_method_name_regular container, name_t
1511
+ end
1512
+
1513
+ return name, container, singleton
1514
+ end
1515
+
1516
+ ##
1517
+ # For the given +container+ and initial name token +name_t+ the method name
1518
+ # is parsed from the token stream for a regular method.
1519
+
1520
+ def parse_method_name_regular container, name_t # :nodoc:
1521
+ if :on_op == name_t[:kind] && (%w{* & [] []= <<}.include?(name_t[:text])) then
1522
+ name_t[:text]
1523
+ else
1524
+ unless [:on_kw, :on_const, :on_ident].include?(name_t[:kind]) then
1525
+ warn "expected method name token, . or ::, got #{name_t.inspect}"
1526
+ skip_method container
1527
+ return
1528
+ end
1529
+ name_t[:text]
1530
+ end
1531
+ end
1532
+
1533
+ ##
1534
+ # For the given +container+ and initial name token +name_t+ the method name
1535
+ # and the new +container+ (if necessary) are parsed from the token stream
1536
+ # for a singleton method.
1537
+
1538
+ def parse_method_name_singleton container, name_t # :nodoc:
1539
+ skip_tkspace
1540
+ name_t2 = get_tk
1541
+
1542
+ if (:on_kw == name_t[:kind] && 'self' == name_t[:text]) || (:on_op == name_t[:kind] && '%' == name_t[:text]) then
1543
+ # NOTE: work around '[' being consumed early
1544
+ if :on_lbracket == name_t2[:kind]
1545
+ get_tk
1546
+ name = '[]'
1547
+ else
1548
+ name = name_t2[:text]
1549
+ end
1550
+ elsif :on_const == name_t[:kind] then
1551
+ name = name_t2[:text]
1552
+
1553
+ container = get_method_container container, name_t
1554
+
1555
+ return unless container
1556
+
1557
+ name
1558
+ elsif :on_ident == name_t[:kind] || :on_ivar == name_t[:kind] || :on_gvar == name_t[:kind] then
1559
+ parse_method_dummy container
1560
+
1561
+ name = nil
1562
+ elsif (:on_kw == name_t[:kind]) && ('true' == name_t[:text] || 'false' == name_t[:text] || 'nil' == name_t[:text]) then
1563
+ klass_name = "#{name_t[:text].capitalize}Class"
1564
+ container = @store.find_class_named klass_name
1565
+ container ||= @top_level.add_class RDoc::NormalClass, klass_name
1566
+
1567
+ name = name_t2[:text]
1568
+ else
1569
+ warn "unexpected method name token #{name_t.inspect}"
1570
+ # break
1571
+ skip_method container
1572
+
1573
+ name = nil
1574
+ end
1575
+
1576
+ return name, container
1577
+ end
1578
+
1579
+ ##
1580
+ # Extracts +yield+ parameters from +method+
1581
+
1582
+ def parse_method_or_yield_parameters(method = nil,
1583
+ modifiers = RDoc::METHOD_MODIFIERS)
1584
+ skip_tkspace_without_nl
1585
+ tk = get_tk
1586
+ end_token = get_end_token tk
1587
+ return '' unless end_token
1588
+
1589
+ nest = 0
1590
+ continue = false
1591
+
1592
+ while tk != nil do
1593
+ case tk[:kind]
1594
+ when :on_semicolon then
1595
+ break if nest == 0
1596
+ when :on_lbracket then
1597
+ nest += 1
1598
+ when :on_rbracket then
1599
+ nest -= 1
1600
+ when :on_lbrace then
1601
+ nest += 1
1602
+ when :on_rbrace then
1603
+ nest -= 1
1604
+ if nest <= 0
1605
+ # we might have a.each { |i| yield i }
1606
+ unget_tk(tk) if nest < 0
1607
+ break
1608
+ end
1609
+ when :on_lparen then
1610
+ nest += 1
1611
+ when end_token[:kind] then
1612
+ if end_token[:kind] == :on_rparen
1613
+ nest -= 1
1614
+ break if nest <= 0
1615
+ else
1616
+ break
1617
+ end
1618
+ when :on_rparen then
1619
+ nest -= 1
1620
+ when :on_comment, :on_embdoc then
1621
+ @read.pop
1622
+ if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
1623
+ (!continue or (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) != 0) then
1624
+ if method && method.block_params.nil? then
1625
+ unget_tk tk
1626
+ read_documentation_modifiers method, modifiers
1627
+ end
1628
+ break if !continue and nest <= 0
1629
+ end
1630
+ when :on_comma then
1631
+ continue = true
1632
+ when :on_ident then
1633
+ continue = false if continue
1634
+ end
1635
+ tk = get_tk
1636
+ end
1637
+
1638
+ get_tkread_clean(/\s+/, ' ')
1639
+ end
1640
+
1641
+ ##
1642
+ # Capture the method's parameters. Along the way, look for a comment
1643
+ # containing:
1644
+ #
1645
+ # # yields: ....
1646
+ #
1647
+ # and add this as the block_params for the method
1648
+
1649
+ def parse_method_parameters method
1650
+ res = parse_method_or_yield_parameters method
1651
+
1652
+ res = "(#{res})" unless res =~ /\A\(/
1653
+ method.params = res unless method.params
1654
+
1655
+ return if method.block_params
1656
+
1657
+ skip_tkspace_without_nl
1658
+ read_documentation_modifiers method, RDoc::METHOD_MODIFIERS
1659
+ end
1660
+
1661
+ ##
1662
+ # Parses an RDoc::NormalModule in +container+ with +comment+
1663
+
1664
+ def parse_module container, single, tk, comment
1665
+ container, name_t, = get_class_or_module container
1666
+
1667
+ name = name_t[:text]
1668
+
1669
+ mod = container.add_module RDoc::NormalModule, name
1670
+ mod.ignore unless container.document_children
1671
+ record_location mod
1672
+
1673
+ read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
1674
+ mod.add_comment comment, @top_level
1675
+ parse_statements mod
1676
+
1677
+ # after end modifiers
1678
+ read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
1679
+
1680
+ @stats.add_module mod
1681
+ end
1682
+
1683
+ ##
1684
+ # Parses an RDoc::Require in +context+ containing +comment+
1685
+
1686
+ def parse_require(context, comment)
1687
+ skip_tkspace_comment
1688
+ tk = get_tk
1689
+
1690
+ if :on_lparen == tk[:kind] then
1691
+ skip_tkspace_comment
1692
+ tk = get_tk
1693
+ end
1694
+
1695
+ name = tk[:text][1..-2] if :on_tstring == tk[:kind]
1696
+
1697
+ if name then
1698
+ @top_level.add_require RDoc::Require.new(name, comment)
1699
+ else
1700
+ unget_tk tk
1701
+ end
1702
+ end
1703
+
1704
+ ##
1705
+ # Parses a rescue
1706
+
1707
+ def parse_rescue
1708
+ skip_tkspace_without_nl
1709
+
1710
+ while tk = get_tk
1711
+ case tk[:kind]
1712
+ when :on_nl, :on_semicolon, :on_comment then
1713
+ break
1714
+ when :on_comma then
1715
+ skip_tkspace_without_nl
1716
+
1717
+ get_tk if :on_nl == peek_tk[:kind]
1718
+ end
1719
+
1720
+ skip_tkspace_without_nl
1721
+ end
1722
+ end
1723
+
1724
+ ##
1725
+ # Retrieve comment body without =begin/=end
1726
+
1727
+ def retrieve_comment_body(tk)
1728
+ if :on_embdoc == tk[:kind]
1729
+ tk[:text].gsub(/\A=begin.*\n/, '').gsub(/=end\n?\z/, '')
1730
+ else
1731
+ tk[:text]
1732
+ end
1733
+ end
1734
+
1735
+ ##
1736
+ # The core of the Ruby parser.
1737
+
1738
+ def parse_statements(container, single = NORMAL, current_method = nil,
1739
+ comment = new_comment(''))
1740
+ raise 'no' unless RDoc::Comment === comment
1741
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
1742
+
1743
+ nest = 1
1744
+ save_visibility = container.visibility
1745
+
1746
+ non_comment_seen = true
1747
+
1748
+ while tk = get_tk do
1749
+ keep_comment = false
1750
+ try_parse_comment = false
1751
+
1752
+ non_comment_seen = true unless (:on_comment == tk[:kind] or :on_embdoc == tk[:kind])
1753
+
1754
+ case tk[:kind]
1755
+ when :on_nl, :on_ignored_nl, :on_comment, :on_embdoc then
1756
+ if :on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
1757
+ skip_tkspace
1758
+ tk = get_tk
1759
+ else
1760
+ past_tokens = @read.size > 1 ? @read[0..-2] : []
1761
+ nl_position = 0
1762
+ past_tokens.reverse.each_with_index do |read_tk, i|
1763
+ if read_tk =~ /^\n$/ then
1764
+ nl_position = (past_tokens.size - 1) - i
1765
+ break
1766
+ elsif read_tk =~ /^#.*\n$/ then
1767
+ nl_position = ((past_tokens.size - 1) - i) + 1
1768
+ break
1769
+ end
1770
+ end
1771
+ comment_only_line = past_tokens[nl_position..-1].all?{ |c| c =~ /^\s+$/ }
1772
+ unless comment_only_line then
1773
+ tk = get_tk
1774
+ end
1775
+ end
1776
+
1777
+ if tk and (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) then
1778
+ if non_comment_seen then
1779
+ # Look for RDoc in a comment about to be thrown away
1780
+ non_comment_seen = parse_comment container, tk, comment unless
1781
+ comment.empty?
1782
+
1783
+ comment = ''
1784
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
1785
+ end
1786
+
1787
+ line_no = nil
1788
+ while tk and (:on_comment == tk[:kind] or :on_embdoc == tk[:kind]) do
1789
+ comment_body = retrieve_comment_body(tk)
1790
+ line_no = tk[:line_no] if comment.empty?
1791
+ comment += comment_body
1792
+ comment << "\n" unless comment_body =~ /\n\z/
1793
+
1794
+ if comment_body.size > 1 && comment_body =~ /\n\z/ then
1795
+ skip_tkspace_without_nl # leading spaces
1796
+ end
1797
+ tk = get_tk
1798
+ end
1799
+
1800
+ comment = new_comment comment, line_no
1801
+
1802
+ unless comment.empty? then
1803
+ look_for_directives_in container, comment
1804
+
1805
+ if container.done_documenting then
1806
+ throw :eof if RDoc::TopLevel === container
1807
+ container.ongoing_visibility = save_visibility
1808
+ end
1809
+ end
1810
+
1811
+ keep_comment = true
1812
+ else
1813
+ non_comment_seen = true
1814
+ end
1815
+
1816
+ unget_tk tk
1817
+ keep_comment = true
1818
+ container.current_line_visibility = nil
1819
+
1820
+ when :on_kw then
1821
+ case tk[:text]
1822
+ when 'class' then
1823
+ parse_class container, single, tk, comment
1824
+
1825
+ when 'module' then
1826
+ parse_module container, single, tk, comment
1827
+
1828
+ when 'def' then
1829
+ parse_method container, single, tk, comment
1830
+
1831
+ when 'alias' then
1832
+ parse_alias container, single, tk, comment unless current_method
1833
+
1834
+ when 'yield' then
1835
+ if current_method.nil? then
1836
+ warn "Warning: yield outside of method" if container.document_self
1837
+ else
1838
+ parse_yield container, single, tk, current_method
1839
+ end
1840
+
1841
+ when 'until', 'while' then
1842
+ if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0
1843
+ nest += 1
1844
+ skip_optional_do_after_expression
1845
+ end
1846
+
1847
+ # Until and While can have a 'do', which shouldn't increase the nesting.
1848
+ # We can't solve the general case, but we can handle most occurrences by
1849
+ # ignoring a do at the end of a line.
1850
+
1851
+ # 'for' is trickier
1852
+ when 'for' then
1853
+ nest += 1
1854
+ skip_for_variable
1855
+ skip_optional_do_after_expression
1856
+
1857
+ when 'case', 'do', 'if', 'unless', 'begin' then
1858
+ if (tk[:state] & RDoc::Parser::RipperStateLex::EXPR_LABEL) == 0
1859
+ nest += 1
1860
+ end
1861
+
1862
+ when 'super' then
1863
+ current_method.calls_super = true if current_method
1864
+
1865
+ when 'rescue' then
1866
+ parse_rescue
1867
+
1868
+ when 'end' then
1869
+ nest -= 1
1870
+ if nest == 0 then
1871
+ container.ongoing_visibility = save_visibility
1872
+
1873
+ parse_comment container, tk, comment unless comment.empty?
1874
+
1875
+ return
1876
+ end
1877
+ end
1878
+
1879
+ when :on_const then
1880
+ unless parse_constant container, tk, comment, current_method then
1881
+ try_parse_comment = true
1882
+ end
1883
+
1884
+ when :on_ident then
1885
+ if nest == 1 and current_method.nil? then
1886
+ keep_comment = parse_identifier container, single, tk, comment
1887
+ end
1888
+
1889
+ case tk[:text]
1890
+ when "require" then
1891
+ parse_require container, comment
1892
+ when "include" then
1893
+ parse_extend_or_include RDoc::Include, container, comment
1894
+ when "extend" then
1895
+ parse_extend_or_include RDoc::Extend, container, comment
1896
+ end
1897
+
1898
+ else
1899
+ try_parse_comment = nest == 1
1900
+ end
1901
+
1902
+ if try_parse_comment then
1903
+ non_comment_seen = parse_comment container, tk, comment unless
1904
+ comment.empty?
1905
+
1906
+ keep_comment = false
1907
+ end
1908
+
1909
+ unless keep_comment then
1910
+ comment = new_comment ''
1911
+ comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
1912
+ container.params = nil
1913
+ container.block_params = nil
1914
+ end
1915
+
1916
+ consume_trailing_spaces
1917
+ end
1918
+
1919
+ container.params = nil
1920
+ container.block_params = nil
1921
+ end
1922
+
1923
+ ##
1924
+ # Parse up to +no+ symbol arguments
1925
+
1926
+ def parse_symbol_arg(no = nil)
1927
+ skip_tkspace_comment
1928
+
1929
+ tk = get_tk
1930
+ if tk[:kind] == :on_lparen
1931
+ parse_symbol_arg_paren no
1932
+ else
1933
+ parse_symbol_arg_space no, tk
1934
+ end
1935
+ end
1936
+
1937
+ ##
1938
+ # Parses up to +no+ symbol arguments surrounded by () and places them in
1939
+ # +args+.
1940
+
1941
+ def parse_symbol_arg_paren no # :nodoc:
1942
+ args = []
1943
+
1944
+ loop do
1945
+ skip_tkspace_comment
1946
+ if tk1 = parse_symbol_in_arg
1947
+ args.push tk1
1948
+ break if no and args.size >= no
1949
+ end
1950
+
1951
+ skip_tkspace_comment
1952
+ case (tk2 = get_tk)[:kind]
1953
+ when :on_rparen
1954
+ break
1955
+ when :on_comma
1956
+ else
1957
+ warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC
1958
+ break
1959
+ end
1960
+ end
1961
+
1962
+ args
1963
+ end
1964
+
1965
+ ##
1966
+ # Parses up to +no+ symbol arguments separated by spaces and places them in
1967
+ # +args+.
1968
+
1969
+ def parse_symbol_arg_space no, tk # :nodoc:
1970
+ args = []
1971
+
1972
+ unget_tk tk
1973
+ if tk = parse_symbol_in_arg
1974
+ args.push tk
1975
+ return args if no and args.size >= no
1976
+ end
1977
+
1978
+ loop do
1979
+ skip_tkspace_without_nl
1980
+
1981
+ tk1 = get_tk
1982
+ if tk1.nil? || :on_comma != tk1[:kind] then
1983
+ unget_tk tk1
1984
+ break
1985
+ end
1986
+
1987
+ skip_tkspace_comment
1988
+ if tk = parse_symbol_in_arg
1989
+ args.push tk
1990
+ break if no and args.size >= no
1991
+ end
1992
+ end
1993
+
1994
+ args
1995
+ end
1996
+
1997
+ ##
1998
+ # Returns symbol text from the next token
1999
+
2000
+ def parse_symbol_in_arg
2001
+ tk = get_tk
2002
+ if :on_symbol == tk[:kind] then
2003
+ tk[:text].sub(/^:/, '')
2004
+ elsif :on_tstring == tk[:kind] then
2005
+ tk[:text][1..-2]
2006
+ elsif :on_dstring == tk[:kind] or :on_ident == tk[:kind] then
2007
+ nil # ignore
2008
+ else
2009
+ warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC
2010
+ nil
2011
+ end
2012
+ end
2013
+
2014
+ ##
2015
+ # Parses statements in the top-level +container+
2016
+
2017
+ def parse_top_level_statements container
2018
+ comment = collect_first_comment
2019
+
2020
+ look_for_directives_in container, comment
2021
+
2022
+ throw :eof if container.done_documenting
2023
+
2024
+ @markup = comment.format
2025
+
2026
+ # HACK move if to RDoc::Context#comment=
2027
+ container.comment = comment if container.document_self unless comment.empty?
2028
+
2029
+ parse_statements container, NORMAL, nil, comment
2030
+ end
2031
+
2032
+ ##
2033
+ # Determines the visibility in +container+ from +tk+
2034
+
2035
+ def parse_visibility(container, single, tk)
2036
+ vis_type, vis, singleton = get_visibility_information tk, single
2037
+
2038
+ skip_tkspace_comment false
2039
+
2040
+ ptk = peek_tk
2041
+ # Ryan Davis suggested the extension to ignore modifiers, because he
2042
+ # often writes
2043
+ #
2044
+ # protected unless $TESTING
2045
+ #
2046
+ if [:on_nl, :on_semicolon].include?(ptk[:kind]) || (:on_kw == ptk[:kind] && (['if', 'unless'].include?(ptk[:text]))) then
2047
+ container.ongoing_visibility = vis
2048
+ elsif :on_kw == ptk[:kind] && 'def' == ptk[:text]
2049
+ container.current_line_visibility = vis
2050
+ else
2051
+ update_visibility container, vis_type, vis, singleton
2052
+ end
2053
+ end
2054
+
2055
+ ##
2056
+ # Parses a Module#private_constant or Module#public_constant call from +tk+.
2057
+
2058
+ def parse_constant_visibility(container, single, tk)
2059
+ args = parse_symbol_arg
2060
+ case tk[:text]
2061
+ when 'private_constant'
2062
+ vis = :private
2063
+ when 'public_constant'
2064
+ vis = :public
2065
+ else
2066
+ raise RDoc::Error, 'Unreachable'
2067
+ end
2068
+ container.set_constant_visibility_for args, vis
2069
+ end
2070
+
2071
+ ##
2072
+ # Determines the block parameter for +context+
2073
+
2074
+ def parse_yield(context, single, tk, method)
2075
+ return if method.block_params
2076
+
2077
+ get_tkread
2078
+ method.block_params = parse_method_or_yield_parameters
2079
+ end
2080
+
2081
+ ##
2082
+ # Directives are modifier comments that can appear after class, module, or
2083
+ # method names. For example:
2084
+ #
2085
+ # def fred # :yields: a, b
2086
+ #
2087
+ # or:
2088
+ #
2089
+ # class MyClass # :nodoc:
2090
+ #
2091
+ # We return the directive name and any parameters as a two element array if
2092
+ # the name is in +allowed+. A directive can be found anywhere up to the end
2093
+ # of the current line.
2094
+
2095
+ def read_directive allowed
2096
+ tokens = []
2097
+
2098
+ while tk = get_tk do
2099
+ tokens << tk
2100
+
2101
+ if :on_nl == tk[:kind] or (:on_kw == tk[:kind] && 'def' == tk[:text]) then
2102
+ return
2103
+ elsif :on_comment == tk[:kind] or :on_embdoc == tk[:kind] then
2104
+ return unless tk[:text] =~ /\s*:?([\w-]+):\s*(.*)/
2105
+
2106
+ directive = $1.downcase
2107
+
2108
+ return [directive, $2] if allowed.include? directive
2109
+
2110
+ return
2111
+ end
2112
+ end
2113
+ ensure
2114
+ unless tokens.length == 1 and (:on_comment == tokens.first[:kind] or :on_embdoc == tokens.first[:kind]) then
2115
+ tokens.reverse_each do |token|
2116
+ unget_tk token
2117
+ end
2118
+ end
2119
+ end
2120
+
2121
+ ##
2122
+ # Handles directives following the definition for +context+ (any
2123
+ # RDoc::CodeObject) if the directives are +allowed+ at this point.
2124
+ #
2125
+ # See also RDoc::Markup::PreProcess#handle_directive
2126
+
2127
+ def read_documentation_modifiers context, allowed
2128
+ skip_tkspace_without_nl
2129
+ directive, value = read_directive allowed
2130
+
2131
+ return unless directive
2132
+
2133
+ @preprocess.handle_directive '', directive, value, context do |dir, param|
2134
+ if %w[notnew not_new not-new].include? dir then
2135
+ context.dont_rename_initialize = true
2136
+
2137
+ true
2138
+ end
2139
+ end
2140
+ end
2141
+
2142
+ ##
2143
+ # Records the location of this +container+ in the file for this parser and
2144
+ # adds it to the list of classes and modules in the file.
2145
+
2146
+ def record_location container # :nodoc:
2147
+ case container
2148
+ when RDoc::ClassModule then
2149
+ @top_level.add_to_classes_or_modules container
2150
+ end
2151
+
2152
+ container.record_location @top_level
2153
+ end
2154
+
2155
+ ##
2156
+ # Scans this Ruby file for Ruby constructs
2157
+
2158
+ def scan
2159
+ reset
2160
+
2161
+ catch :eof do
2162
+ begin
2163
+ parse_top_level_statements @top_level
2164
+
2165
+ rescue StandardError => e
2166
+ if @content.include?('<%') and @content.include?('%>') then
2167
+ # Maybe, this is ERB.
2168
+ $stderr.puts "\033[2KRDoc detects ERB file. Skips it for compatibility:"
2169
+ $stderr.puts @file_name
2170
+ return
2171
+ end
2172
+
2173
+ if @scanner_point >= @scanner.size
2174
+ now_line_no = @scanner[@scanner.size - 1][:line_no]
2175
+ else
2176
+ now_line_no = peek_tk[:line_no]
2177
+ end
2178
+ first_tk_index = @scanner.find_index { |tk| tk[:line_no] == now_line_no }
2179
+ last_tk_index = @scanner.find_index { |tk| tk[:line_no] == now_line_no + 1 }
2180
+ last_tk_index = last_tk_index ? last_tk_index - 1 : @scanner.size - 1
2181
+ code = @scanner[first_tk_index..last_tk_index].map{ |t| t[:text] }.join
2182
+
2183
+ $stderr.puts <<-EOF
2184
+
2185
+ #{self.class} failure around line #{now_line_no} of
2186
+ #{@file_name}
2187
+
2188
+ EOF
2189
+
2190
+ unless code.empty? then
2191
+ $stderr.puts code
2192
+ $stderr.puts
2193
+ end
2194
+
2195
+ raise e
2196
+ end
2197
+ end
2198
+
2199
+ @top_level
2200
+ end
2201
+
2202
+ ##
2203
+ # while, until, and for have an optional do
2204
+
2205
+ def skip_optional_do_after_expression
2206
+ skip_tkspace_without_nl
2207
+ tk = get_tk
2208
+
2209
+ b_nest = 0
2210
+ nest = 0
2211
+
2212
+ loop do
2213
+ break unless tk
2214
+ case tk[:kind]
2215
+ when :on_semicolon, :on_nl, :on_ignored_nl then
2216
+ break if b_nest.zero?
2217
+ when :on_lparen then
2218
+ nest += 1
2219
+ when :on_rparen then
2220
+ nest -= 1
2221
+ when :on_kw then
2222
+ case tk[:text]
2223
+ when 'begin'
2224
+ b_nest += 1
2225
+ when 'end'
2226
+ b_nest -= 1
2227
+ when 'do'
2228
+ break if nest.zero?
2229
+ end
2230
+ when :on_comment, :on_embdoc then
2231
+ if b_nest.zero? and "\n" == tk[:text][-1] then
2232
+ break
2233
+ end
2234
+ end
2235
+ tk = get_tk
2236
+ end
2237
+
2238
+ skip_tkspace_without_nl
2239
+
2240
+ get_tk if peek_tk && :on_kw == peek_tk[:kind] && 'do' == peek_tk[:text]
2241
+ end
2242
+
2243
+ ##
2244
+ # skip the var [in] part of a 'for' statement
2245
+
2246
+ def skip_for_variable
2247
+ skip_tkspace_without_nl
2248
+ get_tk
2249
+ skip_tkspace_without_nl
2250
+ tk = get_tk
2251
+ unget_tk(tk) unless :on_kw == tk[:kind] and 'in' == tk[:text]
2252
+ end
2253
+
2254
+ ##
2255
+ # Skips the next method in +container+
2256
+
2257
+ def skip_method container
2258
+ meth = RDoc::AnyMethod.new "", "anon"
2259
+ parse_method_parameters meth
2260
+ parse_statements container, false, meth
2261
+ end
2262
+
2263
+ ##
2264
+ # Skip spaces until a comment is found
2265
+
2266
+ def skip_tkspace_comment(skip_nl = true)
2267
+ loop do
2268
+ skip_nl ? skip_tkspace : skip_tkspace_without_nl
2269
+ next_tk = peek_tk
2270
+ return if next_tk.nil? || (:on_comment != next_tk[:kind] and :on_embdoc != next_tk[:kind])
2271
+ get_tk
2272
+ end
2273
+ end
2274
+
2275
+ ##
2276
+ # Updates visibility in +container+ from +vis_type+ and +vis+.
2277
+
2278
+ def update_visibility container, vis_type, vis, singleton # :nodoc:
2279
+ new_methods = []
2280
+
2281
+ case vis_type
2282
+ when 'module_function' then
2283
+ args = parse_symbol_arg
2284
+ container.set_visibility_for args, :private, false
2285
+
2286
+ container.methods_matching args do |m|
2287
+ s_m = m.dup
2288
+ record_location s_m
2289
+ s_m.singleton = true
2290
+ new_methods << s_m
2291
+ end
2292
+ when 'public_class_method', 'private_class_method' then
2293
+ args = parse_symbol_arg
2294
+
2295
+ container.methods_matching args, true do |m|
2296
+ if m.parent != container then
2297
+ m = m.dup
2298
+ record_location m
2299
+ new_methods << m
2300
+ end
2301
+
2302
+ m.visibility = vis
2303
+ end
2304
+ else
2305
+ args = parse_symbol_arg
2306
+ container.set_visibility_for args, vis, singleton
2307
+ end
2308
+
2309
+ new_methods.each do |method|
2310
+ case method
2311
+ when RDoc::AnyMethod then
2312
+ container.add_method method
2313
+ when RDoc::Attr then
2314
+ container.add_attribute method
2315
+ end
2316
+ method.visibility = vis
2317
+ end
2318
+ end
2319
+
2320
+ ##
2321
+ # Prints +message+ to +$stderr+ unless we're being quiet
2322
+
2323
+ def warn message
2324
+ @options.warn make_message message
2325
+ end
2326
+
2327
+ end