yard 0.9.39 → 0.9.40

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/README.md +18 -21
  4. data/docs/GettingStarted.md +41 -15
  5. data/docs/Tags.md +5 -5
  6. data/docs/WhatsNew.md +59 -7
  7. data/docs/templates/default/yard_tags/html/setup.rb +1 -1
  8. data/lib/yard/autoload.rb +17 -0
  9. data/lib/yard/cli/diff.rb +7 -2
  10. data/lib/yard/code_objects/proxy.rb +1 -1
  11. data/lib/yard/handlers/processor.rb +1 -0
  12. data/lib/yard/handlers/rbs/attribute_handler.rb +43 -0
  13. data/lib/yard/handlers/rbs/base.rb +38 -0
  14. data/lib/yard/handlers/rbs/constant_handler.rb +18 -0
  15. data/lib/yard/handlers/rbs/method_handler.rb +327 -0
  16. data/lib/yard/handlers/rbs/mixin_handler.rb +20 -0
  17. data/lib/yard/handlers/rbs/namespace_handler.rb +26 -0
  18. data/lib/yard/handlers/ruby/attribute_handler.rb +7 -4
  19. data/lib/yard/handlers/ruby/constant_handler.rb +1 -0
  20. data/lib/yard/i18n/locale.rb +1 -1
  21. data/lib/yard/i18n/pot_generator.rb +1 -1
  22. data/lib/yard/parser/rbs/rbs_parser.rb +325 -0
  23. data/lib/yard/parser/rbs/statement.rb +75 -0
  24. data/lib/yard/parser/ruby/ruby_parser.rb +51 -1
  25. data/lib/yard/parser/source_parser.rb +3 -2
  26. data/lib/yard/registry_resolver.rb +7 -0
  27. data/lib/yard/server/library_version.rb +1 -1
  28. data/lib/yard/server/templates/default/fulldoc/html/js/autocomplete.js +208 -12
  29. data/lib/yard/server/templates/default/layout/html/breadcrumb.erb +1 -17
  30. data/lib/yard/server/templates/default/method_details/html/permalink.erb +4 -2
  31. data/lib/yard/server/templates/doc_server/library_list/html/headers.erb +3 -3
  32. data/lib/yard/server/templates/doc_server/library_list/html/library_list.erb +2 -3
  33. data/lib/yard/server/templates/doc_server/processing/html/processing.erb +22 -16
  34. data/lib/yard/tags/directives.rb +7 -0
  35. data/lib/yard/tags/library.rb +3 -3
  36. data/lib/yard/tags/types_explainer.rb +2 -1
  37. data/lib/yard/templates/helpers/base_helper.rb +1 -1
  38. data/lib/yard/templates/helpers/html_helper.rb +15 -4
  39. data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +6 -1
  40. data/lib/yard/templates/helpers/markup/hybrid_markdown.rb +2125 -0
  41. data/lib/yard/templates/helpers/markup_helper.rb +4 -2
  42. data/lib/yard/version.rb +1 -1
  43. data/po/ja.po +82 -82
  44. data/templates/default/fulldoc/html/full_list.erb +4 -4
  45. data/templates/default/fulldoc/html/js/app.js +503 -319
  46. data/templates/default/fulldoc/html/js/full_list.js +310 -213
  47. data/templates/default/layout/html/headers.erb +1 -1
  48. data/templates/default/method/html/header.erb +3 -3
  49. data/templates/default/module/html/defines.erb +3 -3
  50. data/templates/default/module/html/inherited_methods.erb +1 -0
  51. data/templates/default/module/html/method_summary.erb +8 -0
  52. data/templates/default/module/setup.rb +20 -0
  53. data/templates/default/onefile/html/layout.erb +3 -4
  54. data/templates/guide/fulldoc/html/js/app.js +57 -26
  55. data/templates/guide/layout/html/layout.erb +9 -11
  56. metadata +13 -4
@@ -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
- # Register 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
@@ -49,6 +49,7 @@ class YARD::Handlers::Ruby::ConstantHandler < YARD::Handlers::Ruby::Base
49
49
  if (lhs.type == :var_field && lhs[0].type == :const) || lhs.type == :const_path_field
50
50
  klass = create_class(lhs.source, P(:Data))
51
51
  extract_parameters(statement[1]).each do |member|
52
+ next if klass.attributes[:instance][member]
52
53
  klass.attributes[:instance][member] = SymbolHash[:read => nil, :write => nil]
53
54
  create_reader(klass, member)
54
55
  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
 
@@ -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.