yard 0.9.37 → 0.9.41

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -1
  3. data/README.md +27 -30
  4. data/docs/GettingStarted.md +41 -15
  5. data/docs/Tags.md +5 -5
  6. data/docs/Templates.md +5 -4
  7. data/docs/WhatsNew.md +59 -7
  8. data/docs/templates/default/yard_tags/html/setup.rb +1 -1
  9. data/lib/yard/autoload.rb +17 -0
  10. data/lib/yard/cli/diff.rb +7 -2
  11. data/lib/yard/code_objects/base.rb +1 -1
  12. data/lib/yard/code_objects/extra_file_object.rb +1 -0
  13. data/lib/yard/code_objects/proxy.rb +1 -1
  14. data/lib/yard/handlers/base.rb +23 -1
  15. data/lib/yard/handlers/processor.rb +1 -0
  16. data/lib/yard/handlers/rbs/attribute_handler.rb +43 -0
  17. data/lib/yard/handlers/rbs/base.rb +38 -0
  18. data/lib/yard/handlers/rbs/constant_handler.rb +18 -0
  19. data/lib/yard/handlers/rbs/method_handler.rb +327 -0
  20. data/lib/yard/handlers/rbs/mixin_handler.rb +20 -0
  21. data/lib/yard/handlers/rbs/namespace_handler.rb +26 -0
  22. data/lib/yard/handlers/ruby/attribute_handler.rb +7 -4
  23. data/lib/yard/handlers/ruby/constant_handler.rb +24 -6
  24. data/lib/yard/handlers/ruby/legacy/visibility_handler.rb +2 -1
  25. data/lib/yard/handlers/ruby/visibility_handler.rb +1 -0
  26. data/lib/yard/i18n/locale.rb +1 -1
  27. data/lib/yard/i18n/pot_generator.rb +1 -1
  28. data/lib/yard/parser/rbs/rbs_parser.rb +325 -0
  29. data/lib/yard/parser/rbs/statement.rb +75 -0
  30. data/lib/yard/parser/ruby/ast_node.rb +5 -4
  31. data/lib/yard/parser/ruby/legacy/irb/slex.rb +19 -1
  32. data/lib/yard/parser/ruby/legacy/ruby_lex.rb +1 -1
  33. data/lib/yard/parser/ruby/ruby_parser.rb +109 -24
  34. data/lib/yard/parser/source_parser.rb +3 -2
  35. data/lib/yard/registry_resolver.rb +7 -0
  36. data/lib/yard/rubygems/specification.rb +1 -1
  37. data/lib/yard/server/library_version.rb +1 -1
  38. data/lib/yard/server/templates/default/fulldoc/html/js/autocomplete.js +208 -12
  39. data/lib/yard/server/templates/default/layout/html/breadcrumb.erb +1 -17
  40. data/lib/yard/server/templates/default/method_details/html/permalink.erb +4 -2
  41. data/lib/yard/server/templates/doc_server/library_list/html/headers.erb +3 -3
  42. data/lib/yard/server/templates/doc_server/library_list/html/library_list.erb +2 -3
  43. data/lib/yard/server/templates/doc_server/processing/html/processing.erb +22 -16
  44. data/lib/yard/tags/directives.rb +7 -0
  45. data/lib/yard/tags/library.rb +3 -3
  46. data/lib/yard/tags/overload_tag.rb +2 -1
  47. data/lib/yard/tags/tag.rb +1 -1
  48. data/lib/yard/tags/types_explainer.rb +5 -4
  49. data/lib/yard/templates/helpers/base_helper.rb +1 -1
  50. data/lib/yard/templates/helpers/html_helper.rb +21 -6
  51. data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +6 -1
  52. data/lib/yard/templates/helpers/markup/hybrid_markdown.rb +2147 -0
  53. data/lib/yard/templates/helpers/markup/rdoc_markup.rb +2 -0
  54. data/lib/yard/templates/helpers/markup_helper.rb +4 -2
  55. data/lib/yard/version.rb +1 -1
  56. data/po/ja.po +82 -82
  57. data/templates/default/fulldoc/html/css/style.css +33 -15
  58. data/templates/default/fulldoc/html/full_list.erb +4 -4
  59. data/templates/default/fulldoc/html/js/app.js +567 -271
  60. data/templates/default/fulldoc/html/js/full_list.js +341 -211
  61. data/templates/default/layout/html/headers.erb +1 -1
  62. data/templates/default/layout/html/layout.erb +2 -1
  63. data/templates/default/method/html/header.erb +3 -3
  64. data/templates/default/module/html/defines.erb +3 -3
  65. data/templates/default/module/html/inherited_methods.erb +1 -0
  66. data/templates/default/module/html/method_summary.erb +8 -0
  67. data/templates/default/module/setup.rb +20 -0
  68. data/templates/default/onefile/html/layout.erb +3 -4
  69. data/templates/guide/fulldoc/html/js/app.js +57 -26
  70. data/templates/guide/layout/html/layout.erb +9 -11
  71. metadata +18 -8
@@ -0,0 +1,325 @@
1
+ # frozen_string_literal: true
2
+ module YARD
3
+ module Parser
4
+ module RBS
5
+ # Parses RBS (Ruby type signature) files and produces a list of
6
+ # {Statement} objects for post-processing by handlers.
7
+ #
8
+ # RBS is Ruby's official type signature format (introduced in Ruby 3.0).
9
+ # This parser handles: class/module/interface declarations, method
10
+ # signatures, attribute accessors, mixins, and constants.
11
+ #
12
+ # No external gem dependencies are used; the parser is hand-written.
13
+ class RbsParser < YARD::Parser::Base
14
+ # @param source [String] source code to parse
15
+ # @param filename [String] path to the source file
16
+ def initialize(source, filename)
17
+ @source = source
18
+ @filename = filename
19
+ @statements = nil
20
+ end
21
+
22
+ # Parses the source and returns self.
23
+ # @return [RbsParser] self
24
+ def parse
25
+ lines = @source.lines.map { |l| l.chomp }
26
+ @statements, = parse_body(lines, 0, false)
27
+ self
28
+ end
29
+
30
+ # Tokenization is not implemented for RBS.
31
+ def tokenize
32
+ raise NotImplementedError, "RBS parser does not support tokenization"
33
+ end
34
+
35
+ # @return [Array<Statement>] top-level statements for the post-processor
36
+ def enumerator
37
+ @statements
38
+ end
39
+
40
+ private
41
+
42
+ # Parse a sequence of lines, returning statements and the index after the last consumed line.
43
+ #
44
+ # @param lines [Array<String>] source lines
45
+ # @param start [Integer] index to start from (0-based)
46
+ # @param stop_at_end [Boolean] when true, stop parsing when we see a bare `end`
47
+ # @return [Array(Array<Statement>, Integer)] [statements, new_index]
48
+ def parse_body(lines, start, stop_at_end)
49
+ statements = []
50
+ i = start
51
+ pending_comments = []
52
+ pending_start_1 = nil # 1-indexed line number of first pending comment
53
+
54
+ while i < lines.length
55
+ raw = lines[i]
56
+ stripped = raw.strip
57
+
58
+ if stripped =~ /\A#(.*)/
59
+ # Comment line – accumulate into pending docstring.
60
+ # Strip at most one leading space (conventional RBS doc style).
61
+ pending_comments << $1.sub(/\A /, '')
62
+ pending_start_1 ||= i + 1
63
+ i += 1
64
+
65
+ elsif stripped.empty?
66
+ # Blank line resets pending comments.
67
+ pending_comments = []
68
+ pending_start_1 = nil
69
+ i += 1
70
+
71
+ elsif stop_at_end && stripped == 'end'
72
+ # End of enclosing block.
73
+ return [statements, i + 1]
74
+
75
+ else
76
+ stmt, i = parse_statement(lines, i, pending_comments, pending_start_1)
77
+ statements << stmt if stmt
78
+ pending_comments = []
79
+ pending_start_1 = nil
80
+ end
81
+ end
82
+
83
+ [statements, i]
84
+ end
85
+
86
+ def strip_inline_comment(line)
87
+ in_single = false
88
+ in_double = false
89
+ escaped = false
90
+
91
+ line.each_char.with_index do |char, index|
92
+ if escaped
93
+ escaped = false
94
+ next
95
+ end
96
+
97
+ case char
98
+ when "\\"
99
+ escaped = true if in_single || in_double
100
+ when "'"
101
+ in_single = !in_single unless in_double
102
+ when '"'
103
+ in_double = !in_double unless in_single
104
+ when '#'
105
+ return line[0...index].rstrip unless in_single || in_double
106
+ end
107
+ end
108
+
109
+ line.rstrip
110
+ end
111
+
112
+ def sanitized_statement_lines(lines, start_index)
113
+ overrides = { start_index => strip_inline_comment(lines[start_index]) }
114
+
115
+ j = start_index + 1
116
+ while j < lines.length && lines[j].lstrip.start_with?('|')
117
+ overrides[j] = strip_inline_comment(lines[j])
118
+ j += 1
119
+ end
120
+
121
+ overrides
122
+ end
123
+
124
+ # Dispatch a single declaration line.
125
+ def parse_statement(lines, i, comments, comment_start_1)
126
+ sanitized = sanitized_statement_lines(lines, i)
127
+ stripped = sanitized.fetch(i, lines[i]).strip
128
+ line_num = i + 1 # 1-indexed
129
+
130
+ docs = comments.empty? ? nil : comments.join("\n")
131
+ crange = comment_start_1 ? (comment_start_1)..(line_num - 1) : nil
132
+
133
+ case stripped
134
+ when /\Aclass\s/
135
+ parse_namespace(:class, lines, i, docs, crange)
136
+ when /\Amodule\s/
137
+ parse_namespace(:module, lines, i, docs, crange)
138
+ when /\Ainterface\s/
139
+ parse_namespace(:interface, lines, i, docs, crange)
140
+ when /\Adef\s/
141
+ parse_method_def(sanitized, lines, i, docs, crange)
142
+ when /\Aattr_reader\s/
143
+ parse_attr(:attr_reader, lines, i, docs, crange)
144
+ when /\Aattr_writer\s/
145
+ parse_attr(:attr_writer, lines, i, docs, crange)
146
+ when /\Aattr_accessor\s/
147
+ parse_attr(:attr_accessor, lines, i, docs, crange)
148
+ when /\A(include|extend|prepend)\s+(\S+)/
149
+ kind = $1.to_sym
150
+ name = $2.delete(';')
151
+ stmt = Statement.new(
152
+ :type => kind,
153
+ :name => name,
154
+ :mixin_name => name,
155
+ :line => line_num,
156
+ :source => stripped,
157
+ :comments => docs,
158
+ :comments_range => crange
159
+ )
160
+ [stmt, i + 1]
161
+ when /\Aalias\s+(\S+)\s+(\S+)/
162
+ stmt = Statement.new(
163
+ :type => :alias,
164
+ :name => $1,
165
+ :line => line_num,
166
+ :source => stripped,
167
+ :comments => docs,
168
+ :comments_range => crange
169
+ )
170
+ [stmt, i + 1]
171
+ when /\A(public|private|protected)\s*(\z|#)/
172
+ # Visibility modifier – skip silently.
173
+ [nil, i + 1]
174
+ when /\Aend\s*(\z|#)/
175
+ # Stray `end` – skip.
176
+ [nil, i + 1]
177
+ when /\Atype\s/
178
+ # Type alias declaration – nothing to document.
179
+ [nil, i + 1]
180
+ else
181
+ # Constant declaration: `NAME: Type`
182
+ if stripped =~ /\A([A-Z][a-zA-Z0-9_]*(?:::[A-Z][a-zA-Z0-9_]*)*)\s*:\s*(.+)\z/
183
+ stmt = Statement.new(
184
+ :type => :constant,
185
+ :name => $1,
186
+ :attr_rbs_type => $2.strip,
187
+ :line => line_num,
188
+ :source => stripped,
189
+ :comments => docs,
190
+ :comments_range => crange
191
+ )
192
+ [stmt, i + 1]
193
+ else
194
+ [nil, i + 1]
195
+ end
196
+ end
197
+ end
198
+
199
+ def parse_namespace(type, lines, i, docs, crange)
200
+ # Strip trailing inline comment from the declaration line.
201
+ decl = lines[i].strip.sub(/\s*#.*\z/, '')
202
+ line_num = i + 1
203
+
204
+ name = nil
205
+ superclass = nil
206
+
207
+ case type
208
+ when :class
209
+ # class Foo[T] < Bar[String]
210
+ if decl =~ /\Aclass\s+([^\s<\[]+)(\[[^\]]*\])?(?:\s*<\s*(.+))?\z/
211
+ name = $1.strip
212
+ superclass = $3 ? $3.strip : nil
213
+ # Strip generic params from superclass, e.g. "Array[String]" -> "Array"
214
+ superclass.sub!(/\[.*\]\z/, '') if superclass
215
+ else
216
+ return [nil, i + 1]
217
+ end
218
+
219
+ when :module
220
+ # module Foo[T] : SelfType
221
+ if decl =~ /\Amodule\s+([^\s\[(:]+)/
222
+ name = $1.strip
223
+ else
224
+ return [nil, i + 1]
225
+ end
226
+
227
+ when :interface
228
+ # interface _Foo[T]
229
+ if decl =~ /\Ainterface\s+([^\s\[]+)/
230
+ name = $1.strip
231
+ else
232
+ return [nil, i + 1]
233
+ end
234
+ end
235
+
236
+ children, new_i = parse_body(lines, i + 1, true)
237
+ source = lines[i...new_i].join("\n")
238
+
239
+ stmt = Statement.new(
240
+ :type => type,
241
+ :name => name,
242
+ :superclass => superclass,
243
+ :line => line_num,
244
+ :source => source,
245
+ :comments => docs,
246
+ :comments_range => crange,
247
+ :block => children
248
+ )
249
+
250
+ [stmt, new_i]
251
+ end
252
+
253
+ def parse_method_def(sanitized, lines, i, docs, crange)
254
+ stripped = sanitized.fetch(i, lines[i]).strip
255
+ line_num = i + 1
256
+
257
+ # def method_name: overload1
258
+ # | overload2
259
+ # Also handles: def self.method_name: ...
260
+ unless stripped =~ /\Adef\s+(self\.)?(\S+?)\s*:\s*(.*)\z/
261
+ return [nil, i + 1]
262
+ end
263
+
264
+ is_class_side = !$1.nil?
265
+ meth_name = $2
266
+ first_sig = $3.strip
267
+
268
+ sigs = [first_sig]
269
+ j = i + 1
270
+
271
+ # Collect `| overload` continuation lines.
272
+ while j < lines.length
273
+ cont = sanitized.fetch(j, lines[j]).strip
274
+ if cont =~ /\A\|\s*(.*)\z/
275
+ sigs << $1.strip
276
+ j += 1
277
+ else
278
+ break
279
+ end
280
+ end
281
+
282
+ stmt = Statement.new(
283
+ :type => :method_def,
284
+ :name => meth_name,
285
+ :line => line_num,
286
+ :source => lines[i...j].join("\n"),
287
+ :comments => docs,
288
+ :comments_range => crange,
289
+ :signatures => sigs,
290
+ :visibility => is_class_side ? :class : :instance
291
+ )
292
+
293
+ [stmt, j]
294
+ end
295
+
296
+ def parse_attr(type, lines, i, docs, crange)
297
+ stripped = strip_inline_comment(lines[i]).strip
298
+ line_num = i + 1
299
+ keyword = type.to_s
300
+
301
+ # attr_reader [self.] name : Type
302
+ if stripped =~ /\A#{Regexp.escape(keyword)}\s+(self\.)?(\w+)\s*:\s*(.*)\z/
303
+ is_class = !$1.nil?
304
+ attr_name = $2
305
+ attr_type = $3.strip
306
+
307
+ stmt = Statement.new(
308
+ :type => type,
309
+ :name => attr_name,
310
+ :attr_rbs_type => attr_type,
311
+ :line => line_num,
312
+ :source => stripped,
313
+ :comments => docs,
314
+ :comments_range => crange,
315
+ :visibility => is_class ? :class : :instance
316
+ )
317
+ [stmt, i + 1]
318
+ else
319
+ [nil, i + 1]
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+ module YARD
3
+ module Parser
4
+ module RBS
5
+ # Represents a single parsed declaration from an RBS file.
6
+ # Each Statement may have a block of child statements for
7
+ # namespace declarations (class, module, interface).
8
+ class Statement
9
+ # @return [Symbol] declaration type:
10
+ # :class, :module, :interface, :method_def,
11
+ # :attr_reader, :attr_writer, :attr_accessor,
12
+ # :include, :extend, :prepend, :constant, :alias
13
+ attr_reader :type
14
+
15
+ # @return [String] the declaration name
16
+ attr_reader :name
17
+
18
+ # @return [String, nil] the superclass name (for :class)
19
+ attr_reader :superclass
20
+
21
+ # @return [Integer] 1-indexed line number of this statement
22
+ attr_reader :line
23
+
24
+ # @return [String] raw source text of the statement
25
+ attr_reader :source
26
+
27
+ # @return [String, nil] adjacent comment text (the docstring)
28
+ attr_reader :comments
29
+
30
+ # @return [Range, nil] line range of the preceding comments
31
+ attr_reader :comments_range
32
+
33
+ # @return [false] RBS files don't use ## hash-flag comments
34
+ attr_reader :comments_hash_flag
35
+
36
+ # @return [Array<Statement>] child statements for namespace blocks
37
+ attr_reader :block
38
+
39
+ # @return [Array<String>] RBS type signature strings for :method_def
40
+ # Each element is one overload (e.g. "(String name) -> Integer")
41
+ attr_reader :signatures
42
+
43
+ # @return [String, nil] mixin name for :include/:extend/:prepend
44
+ attr_reader :mixin_name
45
+
46
+ # @return [String, nil] RBS type annotation for attrs and constants
47
+ attr_reader :attr_rbs_type
48
+
49
+ # @return [Symbol, nil] :class or :instance scope hint from parser
50
+ attr_reader :visibility
51
+
52
+ def initialize(attrs = {})
53
+ @type = attrs[:type]
54
+ @name = attrs[:name]
55
+ @superclass = attrs[:superclass]
56
+ @line = attrs[:line] || 1
57
+ @source = attrs[:source] || ''
58
+ @comments = attrs[:comments]
59
+ @comments_range = attrs[:comments_range]
60
+ @comments_hash_flag = false
61
+ @block = attrs[:block] || []
62
+ @signatures = attrs[:signatures] || []
63
+ @mixin_name = attrs[:mixin_name]
64
+ @attr_rbs_type = attrs[:attr_rbs_type]
65
+ @visibility = attrs[:visibility]
66
+ end
67
+
68
+ # @return [String] a textual snippet used in error messages
69
+ def show
70
+ source
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -271,7 +271,7 @@ module YARD
271
271
 
272
272
  # @return [Fixnum] the starting line number of the node
273
273
  def line
274
- line_range && line_range.first
274
+ line_range && (line_range.begin || line_range.end)
275
275
  end
276
276
 
277
277
  # @return [String] the first line of source represented by the node.
@@ -345,8 +345,8 @@ module YARD
345
345
  elsif !children.empty?
346
346
  f = children.first
347
347
  l = children.last
348
- self.line_range = Range.new(f.line_range.first, l.line_range.last)
349
- self.source_range = Range.new(f.source_range.first, l.source_range.last)
348
+ self.line_range = Range.new(f.line_range.begin, l.line_range.end)
349
+ self.source_range = Range.new(f.source_range.begin, l.source_range.end)
350
350
  elsif @fallback_line || @fallback_source
351
351
  self.line_range = @fallback_line
352
352
  self.source_range = @fallback_source
@@ -431,7 +431,8 @@ module YARD
431
431
  # shape is (required, optional, rest, more, keyword, keyword_rest, block)
432
432
  # Ruby 3.1 moves :args_forward from rest to keyword_rest
433
433
  args_index = YARD.ruby31? ? -2 : 2
434
- self[args_index].type == :args_forward if self[args_index]
434
+ node = self[args_index]
435
+ node.is_a?(AstNode) && node.type == :args_forward
435
436
  end
436
437
  end
437
438
 
@@ -10,7 +10,25 @@
10
10
  #
11
11
  #
12
12
 
13
- require "irb/notifier"
13
+ begin
14
+ require "irb/notifier"
15
+ rescue LoadError
16
+ module IRB
17
+ module DebugLogger
18
+ def self.pp(*args) end
19
+ end
20
+
21
+ module Notifier
22
+ D_NOMSG = 0x00
23
+ def self.def_notifier(*args) self end
24
+ def self.pp(*args) end
25
+ def self.exec_if(*args, &block) end
26
+ def self.printf(*args) end
27
+ def self.puts(*args) end
28
+ def self.level=(value) end
29
+ end
30
+ end
31
+ end
14
32
 
15
33
  # @private
16
34
  module IRB
@@ -978,7 +978,7 @@ module YARD
978
978
  end
979
979
 
980
980
  def identify_identifier
981
- token = ""
981
+ token = String.new
982
982
  token.concat getc if peek(0) =~ /[$@]/
983
983
  token.concat getc if peek(0) == "@"
984
984