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