yard 0.9.28 → 0.9.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +139 -1
  3. data/LEGAL +29 -1
  4. data/README.md +29 -25
  5. data/docs/GettingStarted.md +41 -15
  6. data/docs/Parser.md +17 -42
  7. data/docs/Tags.md +6 -6
  8. data/docs/Templates.md +5 -4
  9. data/docs/WhatsNew.md +61 -9
  10. data/docs/templates/default/yard_tags/html/setup.rb +1 -1
  11. data/lib/yard/autoload.rb +20 -1
  12. data/lib/yard/cli/command.rb +1 -1
  13. data/lib/yard/cli/diff.rb +7 -2
  14. data/lib/yard/cli/yardoc.rb +1 -1
  15. data/lib/yard/code_objects/base.rb +6 -2
  16. data/lib/yard/code_objects/extra_file_object.rb +1 -0
  17. data/lib/yard/code_objects/macro_object.rb +0 -1
  18. data/lib/yard/code_objects/proxy.rb +1 -1
  19. data/lib/yard/docstring_parser.rb +1 -2
  20. data/lib/yard/handlers/base.rb +23 -1
  21. data/lib/yard/handlers/processor.rb +1 -1
  22. data/lib/yard/handlers/rbs/attribute_handler.rb +79 -0
  23. data/lib/yard/handlers/rbs/base.rb +38 -0
  24. data/lib/yard/handlers/rbs/constant_handler.rb +18 -0
  25. data/lib/yard/handlers/rbs/method_handler.rb +327 -0
  26. data/lib/yard/handlers/rbs/mixin_handler.rb +20 -0
  27. data/lib/yard/handlers/rbs/namespace_handler.rb +26 -0
  28. data/lib/yard/handlers/ruby/attribute_handler.rb +7 -4
  29. data/lib/yard/handlers/ruby/constant_handler.rb +24 -6
  30. data/lib/yard/handlers/ruby/legacy/attribute_handler.rb +1 -1
  31. data/lib/yard/handlers/ruby/legacy/visibility_handler.rb +2 -1
  32. data/lib/yard/handlers/ruby/mixin_handler.rb +13 -6
  33. data/lib/yard/handlers/ruby/visibility_handler.rb +14 -1
  34. data/lib/yard/i18n/locale.rb +2 -2
  35. data/lib/yard/i18n/message.rb +2 -2
  36. data/lib/yard/i18n/messages.rb +1 -1
  37. data/lib/yard/i18n/pot_generator.rb +2 -2
  38. data/lib/yard/logging.rb +116 -61
  39. data/lib/yard/open_struct.rb +67 -0
  40. data/lib/yard/options.rb +1 -1
  41. data/lib/yard/parser/rbs/rbs_parser.rb +325 -0
  42. data/lib/yard/parser/rbs/statement.rb +75 -0
  43. data/lib/yard/parser/ruby/ast_node.rb +5 -4
  44. data/lib/yard/parser/ruby/legacy/irb/slex.rb +19 -1
  45. data/lib/yard/parser/ruby/legacy/ruby_lex.rb +20 -5
  46. data/lib/yard/parser/ruby/ruby_parser.rb +117 -26
  47. data/lib/yard/parser/source_parser.rb +7 -7
  48. data/lib/yard/registry_resolver.rb +9 -1
  49. data/lib/yard/rubygems/specification.rb +1 -1
  50. data/lib/yard/server/commands/base.rb +2 -2
  51. data/lib/yard/server/commands/library_command.rb +8 -8
  52. data/lib/yard/server/commands/static_file_helpers.rb +1 -2
  53. data/lib/yard/server/http_utils.rb +512 -0
  54. data/lib/yard/server/library_version.rb +1 -1
  55. data/lib/yard/server/rack_adapter.rb +13 -5
  56. data/lib/yard/server/templates/default/fulldoc/html/css/custom.css +168 -88
  57. data/lib/yard/server/templates/default/fulldoc/html/js/autocomplete.js +203 -12
  58. data/lib/yard/server/templates/default/layout/html/breadcrumb.erb +1 -17
  59. data/lib/yard/server/templates/default/method_details/html/permalink.erb +4 -2
  60. data/lib/yard/server/templates/doc_server/library_list/html/headers.erb +3 -3
  61. data/lib/yard/server/templates/doc_server/library_list/html/library_list.erb +2 -3
  62. data/lib/yard/server/templates/doc_server/processing/html/processing.erb +22 -16
  63. data/lib/yard/tags/default_factory.rb +1 -0
  64. data/lib/yard/tags/directives.rb +7 -1
  65. data/lib/yard/tags/library.rb +3 -3
  66. data/lib/yard/tags/overload_tag.rb +2 -1
  67. data/lib/yard/tags/tag.rb +4 -3
  68. data/lib/yard/tags/types_explainer.rb +6 -5
  69. data/lib/yard/templates/engine.rb +0 -1
  70. data/lib/yard/templates/helpers/base_helper.rb +1 -1
  71. data/lib/yard/templates/helpers/html_helper.rb +21 -6
  72. data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +6 -1
  73. data/lib/yard/templates/helpers/markup/hybrid_markdown.rb +2147 -0
  74. data/lib/yard/templates/helpers/markup/rdoc_markup.rb +2 -0
  75. data/lib/yard/templates/helpers/markup_helper.rb +4 -2
  76. data/lib/yard/templates/template_options.rb +0 -1
  77. data/lib/yard/version.rb +1 -1
  78. data/po/ja.po +101 -101
  79. data/templates/default/fulldoc/html/css/common.css +1 -1
  80. data/templates/default/fulldoc/html/css/full_list.css +201 -53
  81. data/templates/default/fulldoc/html/css/style.css +991 -399
  82. data/templates/default/fulldoc/html/frames.erb +9 -4
  83. data/templates/default/fulldoc/html/full_list.erb +8 -5
  84. data/templates/default/fulldoc/html/js/app.js +799 -312
  85. data/templates/default/fulldoc/html/js/full_list.js +332 -214
  86. data/templates/default/fulldoc/html/setup.rb +10 -2
  87. data/templates/default/layout/html/headers.erb +1 -1
  88. data/templates/default/layout/html/layout.erb +3 -1
  89. data/templates/default/method/html/header.erb +3 -3
  90. data/templates/default/module/html/defines.erb +3 -3
  91. data/templates/default/module/html/inherited_methods.erb +1 -0
  92. data/templates/default/module/html/method_summary.erb +8 -0
  93. data/templates/default/module/setup.rb +20 -0
  94. data/templates/default/onefile/html/headers.erb +2 -0
  95. data/templates/default/onefile/html/layout.erb +3 -4
  96. data/templates/default/tags/html/example.erb +2 -2
  97. data/templates/default/tags/html/option.erb +1 -1
  98. data/templates/guide/fulldoc/html/css/style.css +347 -97
  99. data/templates/guide/fulldoc/html/js/app.js +61 -33
  100. data/templates/guide/layout/html/layout.erb +69 -72
  101. metadata +21 -60
  102. data/.dockerignore +0 -2
  103. data/.gitattributes +0 -4
  104. data/.github/FUNDING.yml +0 -3
  105. data/.github/ISSUE_TEMPLATE.md +0 -33
  106. data/.github/PULL_REQUEST_TEMPLATE.md +0 -12
  107. data/.github/workflows/ci.yml +0 -30
  108. data/.github/workflows/gem.yml +0 -19
  109. data/.gitignore +0 -14
  110. data/.rspec +0 -2
  111. data/.rubocop.yml +0 -112
  112. data/CODE_OF_CONDUCT.md +0 -15
  113. data/CONTRIBUTING.md +0 -140
  114. data/Dockerfile.samus +0 -28
  115. data/Gemfile +0 -34
  116. data/Rakefile +0 -36
  117. data/SECURITY.md +0 -26
  118. data/benchmarks/builtins_vs_eval.rb +0 -24
  119. data/benchmarks/concat_vs_join.rb +0 -13
  120. data/benchmarks/erb_vs_erubis.rb +0 -54
  121. data/benchmarks/format_args.rb +0 -47
  122. data/benchmarks/generation.rb +0 -38
  123. data/benchmarks/marshal_vs_dbm.rb +0 -64
  124. data/benchmarks/parsing.rb +0 -46
  125. data/benchmarks/pathname_vs_string.rb +0 -51
  126. data/benchmarks/rdoc_vs_yardoc.rb +0 -11
  127. data/benchmarks/registry_store_types.rb +0 -49
  128. data/benchmarks/ri_vs_yri.rb +0 -19
  129. data/benchmarks/ripper_parser.rb +0 -13
  130. data/benchmarks/splat_vs_flatten.rb +0 -13
  131. data/benchmarks/template_erb.rb +0 -23
  132. data/benchmarks/template_format.rb +0 -7
  133. data/benchmarks/template_profile.rb +0 -18
  134. data/benchmarks/yri_cache.rb +0 -20
  135. data/samus.json +0 -49
  136. data/tasks/prepare_tag.rake +0 -45
  137. data/tasks/update_error_map.rake +0 -53
  138. data/yard.gemspec +0 -25
@@ -0,0 +1,327 @@
1
+ # frozen_string_literal: true
2
+ # Handles RBS method definitions (def name: signature).
3
+ #
4
+ # Creates a {YARD::CodeObjects::MethodObject} for each declaration
5
+ # and infers @param, @return, @yield, and @yieldparam tags from the
6
+ # RBS type signature when those tags are absent from the docstring.
7
+ class YARD::Handlers::RBS::MethodHandler < YARD::Handlers::RBS::Base
8
+ handles :method_def
9
+
10
+ process do
11
+ meth_scope = statement.visibility == :class ? :class : :instance
12
+ obj = register MethodObject.new(namespace, statement.name, meth_scope)
13
+ apply_signature_tags(obj, statement.signatures)
14
+
15
+ # For initialize, ensure the return type is the class, not void.
16
+ if statement.name == 'initialize'
17
+ ret_tags = obj.tags(:return)
18
+ if ret_tags.none? || (ret_tags.length == 1 && ret_tags.first.types == ['void'])
19
+ obj.docstring.delete_tags(:return)
20
+ obj.add_tag YARD::Tags::Tag.new(:return, "a new instance of #{namespace.name}",
21
+ [namespace.name.to_s])
22
+ end
23
+ end
24
+ end
25
+
26
+ # Convert an RBS type string to an array of YARD type strings.
27
+ #
28
+ # @param rbs [String] e.g. "String | Integer", "Array[String]", "bool"
29
+ # @return [Array<String>]
30
+ def self.rbs_type_to_yard_types(rbs)
31
+ rbs = rbs.strip
32
+ return ['void'] if rbs == 'void'
33
+ return ['Boolean'] if rbs == 'bool'
34
+ return ['Object'] if rbs == 'untyped'
35
+ return ['nil'] if rbs == 'nil'
36
+
37
+ # Strip outer parentheses: `(String | Integer)` → recurse on inner.
38
+ if rbs.start_with?('(') && rbs.end_with?(')') && bracket_depth(rbs[1..-2]) == 0
39
+ return rbs_type_to_yard_types(rbs[1..-2])
40
+ end
41
+
42
+ # `Type?` is shorthand for `Type | nil` when the ? is outermost.
43
+ if rbs =~ /\A(.+)\?\z/ && bracket_depth($1) == 0
44
+ return rbs_type_to_yard_types($1) + ['nil']
45
+ end
46
+
47
+ split_on_pipe(rbs).map { |t| t.strip }
48
+ end
49
+
50
+ private
51
+
52
+ # Apply tags from all overload signatures to the method object.
53
+ def apply_signature_tags(obj, sigs)
54
+ return if sigs.nil? || sigs.empty?
55
+
56
+ if sigs.length == 1
57
+ # Single signature: add @param and @return directly.
58
+ add_param_return_tags(obj, sigs.first)
59
+ else
60
+ # Multiple signatures: add @overload tags.
61
+ sigs.each { |sig| add_overload_tag(obj, statement.name, sig) }
62
+ end
63
+ end
64
+
65
+ # Add @param / @return / @yield / @yieldparam from a single overload sig.
66
+ def add_param_return_tags(obj, sig)
67
+ parsed = parse_function_type(sig)
68
+
69
+ parsed[:params].each do |p|
70
+ next if p[:block] # block param handled via @yield below
71
+ tag_name = p[:name] ? p[:name].to_s : nil
72
+ next if tag_name && obj.tags(:param).any? { |t| t.name == tag_name }
73
+ obj.add_tag YARD::Tags::Tag.new(:param, '', p[:types], tag_name)
74
+ end
75
+
76
+ if (blk = parsed[:block_param])
77
+ add_yield_tags(obj, blk)
78
+ end
79
+
80
+ unless obj.has_tag?(:return)
81
+ obj.add_tag YARD::Tags::Tag.new(:return, '', parsed[:return_types])
82
+ end
83
+ end
84
+
85
+ # Add an @overload tag for one signature overload.
86
+ def add_overload_tag(obj, meth_name, sig)
87
+ parsed = parse_function_type(sig)
88
+ param_sigs = parsed[:params].reject { |p| p[:block] }.map.with_index do |p, idx|
89
+ p[:name] || "arg#{idx}"
90
+ end
91
+
92
+ # Build the overload tag text: signature line + nested @param/@return lines.
93
+ lines = ["#{meth_name}(#{param_sigs.join(', ')})"]
94
+ parsed[:params].reject { |p| p[:block] }.each_with_index do |p, idx|
95
+ pname = p[:name] || "arg#{idx}"
96
+ lines << " @param #{pname} [#{p[:types].join(', ')}]"
97
+ end
98
+
99
+ if (blk = parsed[:block_param])
100
+ add_yield_tags(obj, blk)
101
+ end
102
+
103
+ lines << " @return [#{parsed[:return_types].join(', ')}]"
104
+ obj.add_tag YARD::Tags::OverloadTag.new(:overload, lines.join("\n"))
105
+ end
106
+
107
+ # Add @yield and @yieldparam tags from a parsed block type.
108
+ def add_yield_tags(obj, blk)
109
+ return if obj.has_tag?(:yield) && obj.has_tag?(:yieldparam)
110
+ obj.add_tag YARD::Tags::Tag.new(:yield, '') unless obj.has_tag?(:yield)
111
+ blk[:params].each_with_index do |p, idx|
112
+ pname = p[:name] || "arg#{idx}"
113
+ next if obj.tags(:yieldparam).any? { |t| t.name == pname }
114
+ obj.add_tag YARD::Tags::Tag.new(:yieldparam, '', p[:types], pname)
115
+ end
116
+ unless obj.has_tag?(:yieldreturn)
117
+ obj.add_tag YARD::Tags::Tag.new(:yieldreturn, '', self.class.rbs_type_to_yard_types(blk[:return_type] || 'void'))
118
+ end
119
+ end
120
+
121
+ # Parse a single RBS function type string (one overload) into its components.
122
+ #
123
+ # @param sig [String] e.g. "(String name, Integer age) -> String"
124
+ # @return [Hash] { :params => [...], :block_param => Hash|nil, :return_types => [...] }
125
+ def parse_function_type(sig)
126
+ sig = sig.strip
127
+ return { :params => [], :block_param => nil, :return_types => ['void'] } if sig.empty?
128
+
129
+ remaining = sig
130
+ params = []
131
+ block_param = nil
132
+
133
+ # 1. Extract positional/keyword params: leading `(...)`.
134
+ if remaining.start_with?('(')
135
+ close = find_matching(remaining, 0, '(', ')')
136
+ raise YARD::Parser::UndocumentableError, "malformed signature (unclosed '('): #{sig}" if close.nil?
137
+ params_str = remaining[1...close]
138
+ remaining = remaining[close + 1..-1].lstrip
139
+ params = parse_params_list(params_str)
140
+ end
141
+
142
+ # 2. Extract block type: `{ ... }`.
143
+ if remaining.start_with?('{')
144
+ close = find_matching(remaining, 0, '{', '}')
145
+ raise YARD::Parser::UndocumentableError, "malformed signature (unclosed '{'): #{sig}" if close.nil?
146
+ block_inner = remaining[1...close]
147
+ remaining = remaining[close + 1..-1].lstrip
148
+ block_param = parse_block_type(block_inner)
149
+ end
150
+
151
+ # 3. Return type after `->`.
152
+ return_types = if remaining =~ /\A->\s*(.*)\z/
153
+ self.class.rbs_type_to_yard_types($1.strip)
154
+ else
155
+ ['void']
156
+ end
157
+
158
+ { :params => params, :block_param => block_param, :return_types => return_types }
159
+ end
160
+
161
+ # Parse a comma-separated parameter list (content inside outer parens).
162
+ def parse_params_list(str)
163
+ str = str.strip
164
+ return [] if str.empty?
165
+
166
+ split_by_comma(str).map { |p| parse_single_param(p.strip) }.compact
167
+ end
168
+
169
+ # Parse one parameter from an RBS param list.
170
+ def parse_single_param(param)
171
+ return nil if param.empty?
172
+
173
+ optional = false
174
+ rest = false
175
+
176
+ # Optional marker `?`.
177
+ if param.start_with?('?') && !param.start_with?('?(')
178
+ optional = true
179
+ param = param[1..-1].lstrip
180
+ end
181
+
182
+ # Double-splat `**` (rest keyword).
183
+ if param.start_with?('**')
184
+ rest = true
185
+ param = param[2..-1].lstrip
186
+ # Single-splat `*` (rest positional).
187
+ elsif param.start_with?('*') && !param.start_with?('*)')
188
+ rest = true
189
+ param = param[1..-1].lstrip
190
+ end
191
+
192
+ # Block-type proc: `^(...)`.
193
+ if param.start_with?('^')
194
+ return { :name => nil, :types => [param], :optional => false, :rest => false, :block => true }
195
+ end
196
+
197
+ # Keyword parameter: `name: Type` or `?name: Type`.
198
+ if param =~ /\A([a-z_]\w*)\s*:\s*(.*)\z/ && !rest
199
+ kw_name = $1
200
+ kw_type = $2.strip
201
+ return { :name => "#{kw_name}:", :types => self.class.rbs_type_to_yard_types(kw_type),
202
+ :optional => optional, :rest => false }
203
+ end
204
+
205
+ # Positional: `Type [param_name]`.
206
+ type_str, param_name = extract_type_and_name(param)
207
+ { :name => param_name, :types => self.class.rbs_type_to_yard_types(type_str),
208
+ :optional => optional, :rest => rest }
209
+ end
210
+
211
+ # Split a type+name string like "Array[String] names" into ["Array[String]", "names"].
212
+ # The name is the trailing lowercase identifier (if any).
213
+ def extract_type_and_name(str)
214
+ str = str.strip
215
+ if str =~ /\A(.*\S)\s+([a-z_]\w*)\z/m
216
+ type_part = $1.strip
217
+ name_part = $2
218
+ # Exclude RBS type keywords from being mistaken for names.
219
+ unless %w[void untyped nil bool top bottom self instance class].include?(name_part)
220
+ return [type_part, name_part] unless type_part.empty?
221
+ end
222
+ end
223
+ [str, nil]
224
+ end
225
+
226
+ # Parse the inside of a `{ ... }` block type, e.g. "(Integer) -> String".
227
+ def parse_block_type(inner)
228
+ inner = inner.strip
229
+ params = []
230
+ ret = nil
231
+
232
+ if inner.start_with?('(')
233
+ close = find_matching(inner, 0, '(', ')')
234
+ raise YARD::Parser::UndocumentableError, "malformed block type (unclosed '('): #{inner}" if close.nil?
235
+ params = parse_params_list(inner[1...close])
236
+ rest = inner[close + 1..-1].lstrip
237
+ else
238
+ rest = inner
239
+ end
240
+
241
+ ret = $1.strip if rest =~ /\A->\s*(.*)\z/
242
+ { :params => params, :return_type => ret }
243
+ end
244
+
245
+ # Find the index of the matching close bracket starting from +start+.
246
+ # @return [nil] if no matching bracket is found (malformed input).
247
+ def find_matching(str, start, open, close)
248
+ depth = 0
249
+ (start...str.length).each do |i|
250
+ case str[i]
251
+ when open then depth += 1
252
+ when close
253
+ depth -= 1
254
+ return i if depth == 0
255
+ end
256
+ end
257
+ nil
258
+ end
259
+
260
+ # Split +str+ on commas that are not inside brackets.
261
+ def split_by_comma(str)
262
+ depth = 0
263
+ parts = []
264
+ cur = String.new('')
265
+ str.each_char do |c|
266
+ case c
267
+ when '(', '[', '{'
268
+ depth += 1
269
+ cur << c
270
+ when ')', ']', '}'
271
+ depth -= 1
272
+ cur << c
273
+ when ','
274
+ if depth == 0
275
+ parts << cur.strip
276
+ cur = String.new('')
277
+ else
278
+ cur << c
279
+ end
280
+ else
281
+ cur << c
282
+ end
283
+ end
284
+ parts << cur.strip unless cur.strip.empty?
285
+ parts
286
+ end
287
+
288
+ # Split +str+ on `|` that are not inside brackets.
289
+ def self.split_on_pipe(str)
290
+ depth = 0
291
+ parts = []
292
+ cur = String.new('')
293
+ str.each_char do |c|
294
+ case c
295
+ when '(', '[', '{'
296
+ depth += 1
297
+ cur << c
298
+ when ')', ']', '}'
299
+ depth -= 1
300
+ cur << c
301
+ when '|'
302
+ if depth == 0
303
+ parts << cur.strip
304
+ cur = String.new('')
305
+ else
306
+ cur << c
307
+ end
308
+ else
309
+ cur << c
310
+ end
311
+ end
312
+ parts << cur.strip unless cur.strip.empty?
313
+ parts
314
+ end
315
+
316
+ # Return the bracket depth of the full string (should be 0 for well-formed types).
317
+ def self.bracket_depth(str)
318
+ depth = 0
319
+ str.each_char do |c|
320
+ case c
321
+ when '(', '[', '{' then depth += 1
322
+ when ')', ']', '}' then depth -= 1
323
+ end
324
+ end
325
+ depth
326
+ end
327
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ # Handles RBS include, extend, and prepend declarations.
3
+ class YARD::Handlers::RBS::MixinHandler < YARD::Handlers::RBS::Base
4
+ handles :include, :extend, :prepend
5
+
6
+ process do
7
+ mixin = P(namespace, statement.mixin_name)
8
+ case statement.type
9
+ when :include
10
+ mixins = namespace.mixins(:instance)
11
+ mixins << mixin unless mixins.include?(mixin)
12
+ when :extend
13
+ mixins = namespace.mixins(:class)
14
+ mixins << mixin unless mixins.include?(mixin)
15
+ when :prepend
16
+ mixins = namespace.mixins(:instance)
17
+ mixins.unshift(mixin) unless mixins.include?(mixin)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ # Handles RBS class, module, and interface declarations by registering
3
+ # the corresponding namespace code objects and recursing into their bodies.
4
+ class YARD::Handlers::RBS::NamespaceHandler < YARD::Handlers::RBS::Base
5
+ handles :class, :module, :interface
6
+ namespace_only
7
+
8
+ process do
9
+ name = statement.name
10
+ type = statement.type
11
+
12
+ obj = case type
13
+ when :class
14
+ klass = register ClassObject.new(namespace, name)
15
+ if (sc = statement.superclass) && !sc.strip.empty?
16
+ klass.superclass = P(namespace, sc)
17
+ klass.superclass.type = :class if klass.superclass.is_a?(Proxy)
18
+ end
19
+ klass
20
+ when :module, :interface
21
+ register ModuleObject.new(namespace, name)
22
+ end
23
+
24
+ parse_block(:namespace => obj)
25
+ end
26
+ end
@@ -31,8 +31,6 @@ class YARD::Handlers::Ruby::AttributeHandler < YARD::Handlers::Ruby::Base
31
31
 
32
32
  # Add all attributes
33
33
  validated_attribute_names(params).each do |name|
34
- namespace.attributes[scope][name] ||= SymbolHash[:read => nil, :write => nil]
35
-
36
34
  # Show their methods as well
37
35
  {:read => name, :write => "#{name}="}.each do |type, meth|
38
36
  if type == :read ? read : write
@@ -52,12 +50,17 @@ class YARD::Handlers::Ruby::AttributeHandler < YARD::Handlers::Ruby::Base
52
50
  register(o)
53
51
  o.docstring = doc if o.docstring.blank?(false)
54
52
 
55
- # Regsiter the object explicitly
56
- namespace.attributes[scope][name][type] = o
53
+ # Register the object explicitly.
54
+ # Use o.scope rather than scope: register() may have changed o.scope
55
+ # via a @!scope directive, so the attribute must be stored under the
56
+ # method's final scope to keep attr_info's lookup consistent.
57
+ namespace.attributes[o.scope][name] ||= SymbolHash[:read => nil, :write => nil]
58
+ namespace.attributes[o.scope][name][type] = o
57
59
  else
58
60
  obj = namespace.children.find {|other| other.name == meth.to_sym && other.scope == scope }
59
61
 
60
62
  # register an existing method as attribute
63
+ namespace.attributes[scope][name] ||= SymbolHash[:read => nil, :write => nil]
61
64
  namespace.attributes[scope][name][type] = obj if obj
62
65
  end
63
66
  end
@@ -9,6 +9,9 @@ class YARD::Handlers::Ruby::ConstantHandler < YARD::Handlers::Ruby::Base
9
9
  if statement[1].call? && statement[1][0][0] == s(:const, "Struct") &&
10
10
  statement[1][2] == s(:ident, "new")
11
11
  process_structclass(statement)
12
+ elsif statement[1].call? && statement[1][0][0] == s(:const, "Data") &&
13
+ statement[1][2] == s(:ident, "define")
14
+ process_dataclass(statement)
12
15
  elsif statement[0].type == :var_field && statement[0][0].type == :const
13
16
  process_constant(statement)
14
17
  elsif statement[0].type == :const_path_field
@@ -31,20 +34,35 @@ class YARD::Handlers::Ruby::ConstantHandler < YARD::Handlers::Ruby::Base
31
34
  end
32
35
 
33
36
  def process_structclass(statement)
34
- lhs = statement[0][0]
35
- if lhs.type == :const
36
- klass = create_class(lhs[0], P(:Struct))
37
+ lhs = statement[0]
38
+ if (lhs.type == :var_field && lhs[0].type == :const) || lhs.type == :const_path_field
39
+ klass = create_class(lhs.source, P(:Struct))
37
40
  create_attributes(klass, extract_parameters(statement[1]))
38
41
  parse_block(statement[1].block[1], :namespace => klass) unless statement[1].block.nil?
39
42
  else
40
- raise YARD::Parser::UndocumentableError, "Struct assignment to #{statement[0].source}"
43
+ raise YARD::Parser::UndocumentableError, "Struct assignment to #{lhs.source}"
41
44
  end
42
45
  end
43
46
 
44
- # Extract the parameters from the Struct.new AST node, returning them as a list
47
+ def process_dataclass(statement)
48
+ lhs = statement[0]
49
+ if (lhs.type == :var_field && lhs[0].type == :const) || lhs.type == :const_path_field
50
+ klass = create_class(lhs.source, P(:Data))
51
+ extract_parameters(statement[1]).each do |member|
52
+ next if klass.attributes[:instance][member]
53
+ klass.attributes[:instance][member] = SymbolHash[:read => nil, :write => nil]
54
+ create_reader(klass, member)
55
+ end
56
+ parse_block(statement[1].block[1], :namespace => klass) unless statement[1].block.nil?
57
+ else
58
+ raise YARD::Parser::UndocumentableError, "Data assignment to #{lhs.source}"
59
+ end
60
+ end
61
+
62
+ # Extract the parameters from the Struct.new or Data.define AST node, returning them as a list
45
63
  # of strings
46
64
  #
47
- # @param [MethodCallNode] superclass the AST node for the Struct.new call
65
+ # @param [MethodCallNode] superclass the AST node for the Struct.new or Data.define call
48
66
  # @return [Array<String>] the member names to generate methods for
49
67
  def extract_parameters(superclass)
50
68
  return [] unless superclass.parameters
@@ -51,7 +51,7 @@ class YARD::Handlers::Ruby::Legacy::AttributeHandler < YARD::Handlers::Ruby::Leg
51
51
  register(o)
52
52
  o.docstring = doc if o.docstring.blank?(false)
53
53
 
54
- # Regsiter the object explicitly
54
+ # Register the object explicitly
55
55
  namespace.attributes[scope][name][type] = o
56
56
  else
57
57
  obj = namespace.children.find {|other| other.name == meth.to_sym && other.scope == scope }
@@ -8,9 +8,10 @@ class YARD::Handlers::Ruby::Legacy::VisibilityHandler < YARD::Handlers::Ruby::Le
8
8
  vis = statement.tokens.first.text
9
9
  if statement.tokens.size == 1
10
10
  self.visibility = vis
11
+ globals.visibility_origin = :keyword
11
12
  else
12
13
  tokval_list(statement.tokens[2..-1], :attr).each do |name|
13
- MethodObject.new(namespace, name, scope) {|o| o.visibility = vis }
14
+ MethodObject.new(namespace, name, scope) { |o| o.visibility = vis }
14
15
  end
15
16
  end
16
17
  end
@@ -26,22 +26,29 @@ class YARD::Handlers::Ruby::MixinHandler < YARD::Handlers::Ruby::Base
26
26
  raise YARD::Parser::UndocumentableError unless mixin.ref?
27
27
  raise YARD::Parser::UndocumentableError if mixin.first.type == :ident
28
28
 
29
- case obj = Proxy.new(namespace, mixin.source)
30
- when ConstantObject # If a constant is included, use its value as the real object
31
- obj = Proxy.new(namespace, obj.value, :module)
29
+ if mixin.type == :var_ref && mixin[0] == s(:kw, "self")
30
+ obj = namespace
32
31
  else
33
- obj = Proxy.new(namespace, mixin.source, :module)
32
+ case obj = Proxy.new(namespace, mixin.source)
33
+ when ConstantObject # If a constant is included, use its value as the real object
34
+ obj = Proxy.new(namespace, obj.value, :module)
35
+ else
36
+ obj = Proxy.new(namespace, mixin.source, :module)
37
+ end
34
38
  end
35
39
 
36
40
  rec = recipient(mixin)
37
- return if rec.nil? || rec.mixins(scope).include?(obj)
41
+ return if rec.nil?
42
+
43
+ ensure_loaded!(rec)
44
+ return if rec.mixins(scope).include?(obj)
38
45
 
39
46
  shift = statement.method_name(true) == :include ? :unshift : :push
40
47
  rec.mixins(scope).send(shift, obj)
41
48
  end
42
49
 
43
50
  def recipient(mixin)
44
- if statement[0].type == :const_path_ref
51
+ if statement[0].type == :const_path_ref || statement[0].type == :top_const_ref
45
52
  Proxy.new(namespace, statement[0].source)
46
53
  elsif statement[0].type == :var_ref && statement[0][0] != s(:kw, "self")
47
54
  statement[0][0].type == :const ?
@@ -13,10 +13,23 @@ class YARD::Handlers::Ruby::VisibilityHandler < YARD::Handlers::Ruby::Base
13
13
  case statement.type
14
14
  when :var_ref, :vcall
15
15
  self.visibility = ident.first.to_sym
16
- when :fcall, :command
16
+ globals.visibility_origin = :keyword
17
+ when :command
18
+ if RUBY_VERSION >= '3.' && is_attribute_method?(statement.parameters.first)
19
+ parse_block(statement.parameters.first, visibility: ident.first.to_sym)
20
+ return
21
+ end
22
+ process_decorator do |method|
23
+ method.visibility = ident.first if method.respond_to? :visibility=
24
+ end
25
+ when :fcall
17
26
  process_decorator do |method|
18
27
  method.visibility = ident.first if method.respond_to? :visibility=
19
28
  end
20
29
  end
21
30
  end
31
+
32
+ def is_attribute_method?(node)
33
+ node.type == :command && node.jump(:ident).first.to_s =~ /^attr_(accessor|writer|reader)$/
34
+ end
22
35
  end
@@ -24,7 +24,7 @@ module YARD
24
24
 
25
25
  # @return [String] the name of the locale. It used IETF language
26
26
  # tag format +[language[_territory][.codeset][@modifier]]+.
27
- # @see http://tools.ietf.org/rfc/bcp/bcp47.txt
27
+ # @see https://tools.ietf.org/rfc/bcp/bcp47.txt
28
28
  # BCP 47 - Tags for Identifying Languages
29
29
  attr_reader :name
30
30
 
@@ -57,7 +57,7 @@ module YARD
57
57
  end
58
58
 
59
59
  # @param [String] message the translation target message.
60
- # @return [String] translated message. If tarnslation isn't
60
+ # @return [String] translated message. If translation isn't
61
61
  # registered, the +message+ is returned.
62
62
  def translate(message)
63
63
  @messages[message] || message
@@ -8,7 +8,7 @@ module YARD
8
8
  #
9
9
  # @since 0.8.1
10
10
  class Message
11
- # @return [String] the message ID of the trnslation target message.
11
+ # @return [String] the message ID of the translation target message.
12
12
  attr_reader :id
13
13
 
14
14
  # @return [Set] the set of locations. Location is an array of
@@ -18,7 +18,7 @@ module YARD
18
18
  # @return [Set] the set of comments for the messages.
19
19
  attr_reader :comments
20
20
 
21
- # Creates a trasnlate target message for message ID +id+.
21
+ # Creates a translate target message for message ID +id+.
22
22
  #
23
23
  # @param [String] id the message ID of the translate target message.
24
24
  def initialize(id)
@@ -28,7 +28,7 @@ module YARD
28
28
  @messages[id]
29
29
  end
30
30
 
31
- # Registers a {Message}, the mssage ID of which is +id+. If
31
+ # Registers a {Message}, the message ID of which is +id+. If
32
32
  # corresponding +Message+ is already registered, the previously
33
33
  # registered object is returned.
34
34
  #
@@ -60,7 +60,7 @@ module YARD
60
60
  # File.open(po_file_path, "w") do |pot_file|
61
61
  # pot_file.print(pot)
62
62
  # end
63
- # @see http://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
63
+ # @see https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
64
64
  # GNU gettext manual about details of PO file
65
65
  class PotGenerator
66
66
  # Extracted messages.
@@ -113,7 +113,7 @@ module YARD
113
113
  #
114
114
  # Locations of the +Message+ are used to generate the reference
115
115
  # line that is started with "#: ". +relative_base_path+ passed
116
- # when the generater is created is prepended to each path in location.
116
+ # when the generator is created is prepended to each path in location.
117
117
  #
118
118
  # Comments of the +Message+ are used to generate the
119
119
  # translator-comment line that is started with "# ".