yard 0.9.36 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -1
- data/README.md +29 -25
- data/docs/GettingStarted.md +41 -15
- data/docs/Parser.md +17 -42
- data/docs/Tags.md +5 -5
- data/docs/Templates.md +5 -4
- data/docs/WhatsNew.md +59 -7
- data/docs/templates/default/yard_tags/html/setup.rb +1 -1
- data/lib/yard/autoload.rb +18 -0
- data/lib/yard/cli/diff.rb +7 -2
- data/lib/yard/code_objects/base.rb +1 -1
- data/lib/yard/code_objects/extra_file_object.rb +1 -0
- data/lib/yard/code_objects/macro_object.rb +0 -1
- data/lib/yard/code_objects/proxy.rb +1 -1
- data/lib/yard/docstring_parser.rb +0 -1
- data/lib/yard/handlers/base.rb +23 -1
- data/lib/yard/handlers/processor.rb +1 -1
- data/lib/yard/handlers/rbs/attribute_handler.rb +79 -0
- data/lib/yard/handlers/rbs/base.rb +38 -0
- data/lib/yard/handlers/rbs/constant_handler.rb +18 -0
- data/lib/yard/handlers/rbs/method_handler.rb +327 -0
- data/lib/yard/handlers/rbs/mixin_handler.rb +20 -0
- data/lib/yard/handlers/rbs/namespace_handler.rb +26 -0
- data/lib/yard/handlers/ruby/attribute_handler.rb +7 -4
- data/lib/yard/handlers/ruby/constant_handler.rb +24 -6
- data/lib/yard/handlers/ruby/legacy/visibility_handler.rb +2 -1
- data/lib/yard/handlers/ruby/visibility_handler.rb +14 -1
- data/lib/yard/i18n/locale.rb +1 -1
- data/lib/yard/i18n/pot_generator.rb +1 -1
- data/lib/yard/logging.rb +116 -61
- data/lib/yard/open_struct.rb +67 -0
- data/lib/yard/parser/rbs/rbs_parser.rb +325 -0
- data/lib/yard/parser/rbs/statement.rb +75 -0
- data/lib/yard/parser/ruby/ast_node.rb +5 -4
- data/lib/yard/parser/ruby/legacy/irb/slex.rb +19 -1
- data/lib/yard/parser/ruby/legacy/ruby_lex.rb +20 -5
- data/lib/yard/parser/ruby/ruby_parser.rb +109 -24
- data/lib/yard/parser/source_parser.rb +5 -4
- data/lib/yard/registry_resolver.rb +7 -0
- data/lib/yard/rubygems/specification.rb +1 -1
- data/lib/yard/server/commands/base.rb +1 -1
- data/lib/yard/server/library_version.rb +1 -1
- data/lib/yard/server/templates/default/fulldoc/html/css/custom.css +168 -88
- data/lib/yard/server/templates/default/fulldoc/html/js/autocomplete.js +203 -12
- data/lib/yard/server/templates/default/layout/html/breadcrumb.erb +1 -17
- data/lib/yard/server/templates/default/method_details/html/permalink.erb +4 -2
- data/lib/yard/server/templates/doc_server/library_list/html/headers.erb +3 -3
- data/lib/yard/server/templates/doc_server/library_list/html/library_list.erb +2 -3
- data/lib/yard/server/templates/doc_server/processing/html/processing.erb +22 -16
- data/lib/yard/tags/default_factory.rb +1 -0
- data/lib/yard/tags/directives.rb +7 -1
- data/lib/yard/tags/library.rb +3 -3
- data/lib/yard/tags/overload_tag.rb +2 -1
- data/lib/yard/tags/tag.rb +2 -1
- data/lib/yard/tags/types_explainer.rb +5 -4
- data/lib/yard/templates/engine.rb +0 -1
- data/lib/yard/templates/helpers/base_helper.rb +1 -1
- data/lib/yard/templates/helpers/html_helper.rb +21 -6
- data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +6 -1
- data/lib/yard/templates/helpers/markup/hybrid_markdown.rb +2147 -0
- data/lib/yard/templates/helpers/markup/rdoc_markup.rb +2 -0
- data/lib/yard/templates/helpers/markup_helper.rb +4 -2
- data/lib/yard/templates/template_options.rb +0 -1
- data/lib/yard/version.rb +1 -1
- data/po/ja.po +82 -82
- data/templates/default/fulldoc/html/css/common.css +1 -1
- data/templates/default/fulldoc/html/css/full_list.css +201 -53
- data/templates/default/fulldoc/html/css/style.css +991 -399
- data/templates/default/fulldoc/html/full_list.erb +8 -5
- data/templates/default/fulldoc/html/js/app.js +799 -312
- data/templates/default/fulldoc/html/js/full_list.js +332 -214
- data/templates/default/fulldoc/html/setup.rb +10 -2
- data/templates/default/layout/html/headers.erb +1 -1
- data/templates/default/layout/html/layout.erb +3 -1
- data/templates/default/method/html/header.erb +3 -3
- data/templates/default/module/html/defines.erb +3 -3
- data/templates/default/module/html/inherited_methods.erb +1 -0
- data/templates/default/module/html/method_summary.erb +8 -0
- data/templates/default/module/setup.rb +20 -0
- data/templates/default/onefile/html/headers.erb +2 -0
- data/templates/default/onefile/html/layout.erb +3 -4
- data/templates/default/tags/html/example.erb +2 -2
- data/templates/guide/fulldoc/html/css/style.css +347 -97
- data/templates/guide/fulldoc/html/js/app.js +61 -33
- data/templates/guide/layout/html/layout.erb +69 -72
- metadata +19 -8
data/lib/yard/cli/diff.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
require 'tmpdir'
|
|
3
3
|
require 'fileutils'
|
|
4
4
|
require 'open-uri'
|
|
5
|
+
require 'open3'
|
|
5
6
|
|
|
6
7
|
module YARD
|
|
7
8
|
module CLI
|
|
@@ -108,7 +109,11 @@ module YARD
|
|
|
108
109
|
FileUtils.mkdir_p(tmpdir)
|
|
109
110
|
FileUtils.cp_r('.', tmpdir)
|
|
110
111
|
Dir.chdir(tmpdir)
|
|
111
|
-
|
|
112
|
+
out, status = Open3.capture2e('git', 'reset', '--hard', commit)
|
|
113
|
+
log.info("git says: " + out.chomp)
|
|
114
|
+
unless status.success?
|
|
115
|
+
raise "git reset --hard #{commit.inspect} failed with exit status #{status.exitstatus}: #{out}"
|
|
116
|
+
end
|
|
112
117
|
generate_yardoc(tmpdir)
|
|
113
118
|
ensure
|
|
114
119
|
Dir.chdir(@old_path)
|
|
@@ -158,7 +163,7 @@ module YARD
|
|
|
158
163
|
end
|
|
159
164
|
|
|
160
165
|
# Remote gemfile from rubygems.org
|
|
161
|
-
url = "
|
|
166
|
+
url = "https://rubygems.org/downloads/#{gemfile}"
|
|
162
167
|
log.info "Searching for remote gem file #{url}"
|
|
163
168
|
begin
|
|
164
169
|
# Note: In Ruby 2.4.x, URI.open is a private method. After
|
|
@@ -228,7 +228,7 @@ module YARD
|
|
|
228
228
|
# @example Create class Z inside namespace X::Y
|
|
229
229
|
# CodeObjects::Base.new(P("X::Y"), :Z) # or
|
|
230
230
|
# CodeObjects::Base.new(Registry.root, "X::Y")
|
|
231
|
-
# @param [NamespaceObject] namespace the namespace the object belongs in,
|
|
231
|
+
# @param [NamespaceObject, :root, nil] namespace the namespace the object belongs in,
|
|
232
232
|
# {Registry.root} or :root should be provided if it is associated with
|
|
233
233
|
# the top level namespace.
|
|
234
234
|
# @param [Symbol, String] name the name (or complex path) of the object.
|
|
@@ -201,7 +201,7 @@ module YARD
|
|
|
201
201
|
|
|
202
202
|
private
|
|
203
203
|
|
|
204
|
-
# @note this method fixes a bug in 1.9.2:
|
|
204
|
+
# @note this method fixes a bug in 1.9.2: https://gist.github.com/437136
|
|
205
205
|
def to_ary; nil end
|
|
206
206
|
|
|
207
207
|
# Attempts to find the object that this unresolved object
|
data/lib/yard/handlers/base.rb
CHANGED
|
@@ -462,6 +462,18 @@ module YARD
|
|
|
462
462
|
end
|
|
463
463
|
end
|
|
464
464
|
|
|
465
|
+
if docstring.is_a?(String)
|
|
466
|
+
if (m = docstring.match(/^\s*@!?visibility\s+(public|private|protected)\b/m))
|
|
467
|
+
vis_sym = m[1].to_sym
|
|
468
|
+
|
|
469
|
+
if object.nil?
|
|
470
|
+
globals.visibility_origin = :directive
|
|
471
|
+
elsif object.is_a?(CodeObjects::MethodObject)
|
|
472
|
+
object.visibility = vis_sym
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
|
|
465
477
|
register_transitive_tags(object)
|
|
466
478
|
end
|
|
467
479
|
|
|
@@ -511,7 +523,17 @@ module YARD
|
|
|
511
523
|
def register_visibility(object, visibility = self.visibility)
|
|
512
524
|
return unless object.respond_to?(:visibility=)
|
|
513
525
|
return if object.is_a?(NamespaceObject)
|
|
514
|
-
|
|
526
|
+
|
|
527
|
+
if object.is_a?(CodeObjects::MethodObject)
|
|
528
|
+
origin = globals.visibility_origin
|
|
529
|
+
if origin == :keyword
|
|
530
|
+
object.visibility = visibility if object.scope == scope
|
|
531
|
+
else
|
|
532
|
+
object.visibility = visibility
|
|
533
|
+
end
|
|
534
|
+
else
|
|
535
|
+
object.visibility = visibility
|
|
536
|
+
end
|
|
515
537
|
end
|
|
516
538
|
|
|
517
539
|
# Registers the same method information on the module function, if
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
require 'ostruct'
|
|
3
2
|
|
|
4
3
|
module YARD
|
|
5
4
|
module Handlers
|
|
@@ -36,6 +35,7 @@ module YARD
|
|
|
36
35
|
register_handler_namespace :ruby, Ruby
|
|
37
36
|
register_handler_namespace :ruby18, Ruby::Legacy
|
|
38
37
|
register_handler_namespace :c, C
|
|
38
|
+
register_handler_namespace :rbs, RBS
|
|
39
39
|
|
|
40
40
|
# @return [String] the filename
|
|
41
41
|
attr_accessor :file
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Handles RBS attr_reader, attr_writer, and attr_accessor declarations.
|
|
3
|
+
#
|
|
4
|
+
# Registers one or two {YARD::CodeObjects::MethodObject} instances (reader
|
|
5
|
+
# and/or writer) with @return / @param tags derived from the RBS type.
|
|
6
|
+
class YARD::Handlers::RBS::AttributeHandler < YARD::Handlers::RBS::Base
|
|
7
|
+
handles :attr_reader, :attr_writer, :attr_accessor
|
|
8
|
+
|
|
9
|
+
process do
|
|
10
|
+
attr_name = statement.name
|
|
11
|
+
rbs_type = statement.attr_rbs_type
|
|
12
|
+
yard_types = rbs_type ? YARD::Handlers::RBS::MethodHandler.rbs_type_to_yard_types(rbs_type) : nil
|
|
13
|
+
mscope = statement.visibility == :class ? :class : :instance
|
|
14
|
+
|
|
15
|
+
case statement.type
|
|
16
|
+
when :attr_reader
|
|
17
|
+
register_reader(attr_name, yard_types, mscope)
|
|
18
|
+
register_existing_attribute_method(attr_name, "#{attr_name}=", :write, mscope)
|
|
19
|
+
when :attr_writer
|
|
20
|
+
register_existing_attribute_method(attr_name, attr_name, :read, mscope)
|
|
21
|
+
register_writer(attr_name, yard_types, mscope)
|
|
22
|
+
when :attr_accessor
|
|
23
|
+
register_reader(attr_name, yard_types, mscope)
|
|
24
|
+
register_writer(attr_name, yard_types, mscope)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def register_reader(name, types, scope)
|
|
31
|
+
obj = MethodObject.new(namespace, name, scope)
|
|
32
|
+
obj.source ||= "def #{name}\n @#{name}\nend"
|
|
33
|
+
obj.signature ||= "def #{name}"
|
|
34
|
+
obj = register(obj)
|
|
35
|
+
obj.docstring = "Returns the value of attribute #{name}." if obj.docstring.blank?(false)
|
|
36
|
+
apply_tag_types(obj, :return, types)
|
|
37
|
+
namespace.attributes[obj.scope][name] ||= SymbolHash[:read => nil, :write => nil]
|
|
38
|
+
namespace.attributes[obj.scope][name][:read] = obj
|
|
39
|
+
obj
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def register_writer(name, types, scope)
|
|
43
|
+
obj = MethodObject.new(namespace, "#{name}=", scope)
|
|
44
|
+
obj.parameters = [['value', nil]]
|
|
45
|
+
obj.source ||= "def #{name}=(value)\n @#{name} = value\nend"
|
|
46
|
+
obj.signature ||= "def #{name}=(value)"
|
|
47
|
+
obj = register(obj)
|
|
48
|
+
obj.docstring = "Sets the attribute #{name}\n@param value the value to set the attribute #{name} to." if obj.docstring.blank?(false)
|
|
49
|
+
apply_tag_types(obj, :param, types, "value")
|
|
50
|
+
namespace.attributes[obj.scope][name] ||= SymbolHash[:read => nil, :write => nil]
|
|
51
|
+
namespace.attributes[obj.scope][name][:write] = obj
|
|
52
|
+
obj
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def register_existing_attribute_method(attr_name, meth_name, type, scope)
|
|
56
|
+
namespace.attributes[scope][attr_name] ||= SymbolHash[:read => nil, :write => nil]
|
|
57
|
+
return if namespace.attributes[scope][attr_name][type]
|
|
58
|
+
|
|
59
|
+
obj = namespace.children.find do |other|
|
|
60
|
+
other.name == meth_name.to_sym && other.scope == scope
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
namespace.attributes[scope][attr_name][type] = obj if obj
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def apply_tag_types(obj, tag_name, types, tag_param_name = nil)
|
|
67
|
+
return unless types
|
|
68
|
+
|
|
69
|
+
tag = obj.tags(tag_name).find do |existing_tag|
|
|
70
|
+
existing_tag.name == tag_param_name
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if tag
|
|
74
|
+
tag.types ||= types
|
|
75
|
+
else
|
|
76
|
+
obj.add_tag YARD::Tags::Tag.new(tag_name, '', types, tag_param_name)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module YARD
|
|
3
|
+
module Handlers
|
|
4
|
+
# Handlers for RBS (Ruby type signature) files.
|
|
5
|
+
module RBS
|
|
6
|
+
# Base class for all RBS handlers.
|
|
7
|
+
# Handlers match on the {Parser::RBS::Statement#type} symbol of the
|
|
8
|
+
# current statement and process it to create or annotate code objects.
|
|
9
|
+
class Base < Handlers::Base
|
|
10
|
+
# @return [Boolean] whether this handler matches the given statement
|
|
11
|
+
def self.handles?(statement, _processor)
|
|
12
|
+
handlers.any? do |matcher|
|
|
13
|
+
case matcher
|
|
14
|
+
when Symbol
|
|
15
|
+
statement.type == matcher
|
|
16
|
+
when String
|
|
17
|
+
statement.type.to_s == matcher
|
|
18
|
+
when Regexp
|
|
19
|
+
(statement.source || '') =~ matcher
|
|
20
|
+
else
|
|
21
|
+
false
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Recurse into the body of a namespace statement.
|
|
27
|
+
# @param opts [Hash] state overrides
|
|
28
|
+
# @see #push_state
|
|
29
|
+
def parse_block(opts = {})
|
|
30
|
+
return if statement.block.nil? || statement.block.empty?
|
|
31
|
+
push_state(opts) do
|
|
32
|
+
parser.process(statement.block)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Handles RBS constant declarations: `Name: Type`
|
|
3
|
+
class YARD::Handlers::RBS::ConstantHandler < YARD::Handlers::RBS::Base
|
|
4
|
+
handles :constant
|
|
5
|
+
|
|
6
|
+
process do
|
|
7
|
+
obj = register ConstantObject.new(namespace, statement.name)
|
|
8
|
+
if statement.attr_rbs_type && !obj.has_tag?(:return)
|
|
9
|
+
obj.add_tag YARD::Tags::Tag.new(:return, '', rbs_types(statement.attr_rbs_type))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def rbs_types(type_str)
|
|
16
|
+
YARD::Handlers::RBS::MethodHandler.rbs_type_to_yard_types(type_str)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -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
|
-
|
|
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
|