gitlab-rdoc 6.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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