yard-sorbet 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/yard-sorbet/directives.rb +21 -19
- data/lib/yard-sorbet/handlers/abstract_dsl_handler.rb +21 -17
- data/lib/yard-sorbet/handlers/enums_handler.rb +24 -18
- data/lib/yard-sorbet/handlers/include_handler.rb +37 -0
- data/lib/yard-sorbet/handlers/mixes_in_class_methods_handler.rb +22 -0
- data/lib/yard-sorbet/handlers/sig_handler.rb +54 -84
- data/lib/yard-sorbet/handlers/struct_class_handler.rb +72 -0
- data/lib/yard-sorbet/handlers/struct_class_handler.rbi +5 -0
- data/lib/yard-sorbet/handlers/struct_prop_handler.rb +72 -0
- data/lib/yard-sorbet/handlers.rb +9 -4
- data/lib/yard-sorbet/node_utils.rb +50 -56
- data/lib/yard-sorbet/sig_to_yard.rb +108 -113
- data/lib/yard-sorbet/t_struct_prop.rb +13 -0
- data/lib/yard-sorbet/tag_utils.rb +44 -0
- data/lib/yard-sorbet/version.rb +2 -1
- data/lib/yard-sorbet.rb +3 -3
- metadata +37 -17
- data/lib/yard-sorbet/handlers/struct_handler.rb +0 -109
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 809250a1720fa07f83c0a4545ef5addf33f5a2d4be0dc21e2ca69bd63730bd66
|
4
|
+
data.tar.gz: 7f8381d22e69b630627cc3a833ddde59938522800ea7d2966bd98ae39fe33a35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf72fdb12fb92b88414edd595484734175d2d8f2a6be4030414e8871d858de62c077c862f64aa738911b9ea2d90a2ae616e986c128e35ceb5a0de6ed052a0576
|
7
|
+
data.tar.gz: 5f04cb364f05793cd5112ebfcd2681291aedad3e851c1f9ba8e5302750fd16446d61e3e9862a1634f8d472a7ad3fb2325fcffde8789e6df9a34d4484c055fba2
|
@@ -1,28 +1,30 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module YARDSorbet
|
5
|
+
# Extract & re-add directives to a docstring
|
6
|
+
module Directives
|
7
|
+
extend T::Sig
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
sig { params(docstring: T.nilable(String)).returns([YARD::Docstring, T::Array[String]]) }
|
10
|
+
def self.extract_directives(docstring)
|
11
|
+
parser = YARD::DocstringParser.new.parse(docstring)
|
12
|
+
# Directives are already parsed at this point, and there doesn't
|
13
|
+
# seem to be an API to tweeze them from one node to another without
|
14
|
+
# managing YARD internal state. Instead, we just extract them from
|
15
|
+
# the raw text and re-attach them.
|
16
|
+
directives = parser.raw_text&.split("\n")&.select do |line|
|
17
|
+
line.start_with?('@!')
|
18
|
+
end || []
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
[parser.to_docstring, directives]
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
sig { params(docstring: String, directives: T::Array[String]).void }
|
24
|
+
def self.add_directives(docstring, directives)
|
25
|
+
directives.each do |directive|
|
26
|
+
docstring.concat("\n#{directive}")
|
27
|
+
end
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
@@ -1,26 +1,30 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module YARDSorbet
|
5
|
+
module Handlers
|
6
|
+
# Apllies an `@abstract` tag to `abstract!`/`interface!` modules (if not alerady present).
|
7
|
+
class AbstractDSLHandler < YARD::Handlers::Ruby::Base
|
8
|
+
extend T::Sig
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
handles method_call(:abstract!), method_call(:interface!)
|
11
|
+
namespace_only
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
# The text accompanying the `@abstract` tag.
|
14
|
+
# @see https://github.com/lsegal/yard/blob/main/templates/default/docstring/html/abstract.erb
|
15
|
+
# The `@abstract` tag template
|
16
|
+
TAG_TEXT = 'Subclasses must implement the `abstract` methods below.'
|
17
|
+
# Extra text for class namespaces
|
18
|
+
CLASS_TAG_TEXT = T.let("It cannont be directly instantiated. #{TAG_TEXT}", String)
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
sig { void }
|
21
|
+
def process
|
22
|
+
return if namespace.has_tag?(:abstract)
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
text = namespace.is_a?(YARD::CodeObjects::ClassObject) ? CLASS_TAG_TEXT : TAG_TEXT
|
25
|
+
tag = YARD::Tags::Tag.new(:abstract, text)
|
26
|
+
namespace.add_tag(tag)
|
27
|
+
end
|
28
|
+
end
|
25
29
|
end
|
26
30
|
end
|
@@ -1,28 +1,34 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module YARDSorbet
|
5
|
+
module Handlers
|
6
|
+
# Handle `enums` calls, registering enum values as constants
|
7
|
+
class EnumsHandler < YARD::Handlers::Ruby::Base
|
8
|
+
extend T::Sig
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
handles method_call(:enums)
|
11
|
+
namespace_only
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
sig { void }
|
14
|
+
def process
|
15
|
+
statement.traverse do |node|
|
16
|
+
if const_assign_node?(node)
|
17
|
+
register YARD::CodeObjects::ConstantObject.new(namespace, node.first.source) do |obj|
|
18
|
+
obj.docstring = node.docstring
|
19
|
+
obj.source = node
|
20
|
+
obj.value = node.last.source
|
21
|
+
end
|
22
|
+
end
|
19
23
|
end
|
20
24
|
end
|
21
|
-
end
|
22
|
-
end
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
26
|
+
private
|
27
|
+
|
28
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Boolean) }
|
29
|
+
def const_assign_node?(node)
|
30
|
+
node.type == :assign && node[0][0].type == :const
|
31
|
+
end
|
32
|
+
end
|
27
33
|
end
|
28
34
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module YARDSorbet
|
5
|
+
module Handlers
|
6
|
+
# Extends any modules included via `mixes_in_class_methods`
|
7
|
+
# @see https://sorbet.org/docs/abstract#interfaces-and-the-included-hook
|
8
|
+
# Sorbet `mixes_in_class_methods` documentation
|
9
|
+
class IncludeHandler < YARD::Handlers::Ruby::Base
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
handles method_call(:include)
|
13
|
+
namespace_only
|
14
|
+
|
15
|
+
sig { void }
|
16
|
+
def process
|
17
|
+
return unless extra_state.mix_in_class_methods
|
18
|
+
|
19
|
+
statement.parameters(false).each do |mixin|
|
20
|
+
obj = YARD::CodeObjects::Proxy.new(namespace, mixin.source)
|
21
|
+
class_methods_namespace = extra_state.mix_in_class_methods[obj.to_s]
|
22
|
+
next unless class_methods_namespace
|
23
|
+
|
24
|
+
included_in.mixins(:class) << YARD::CodeObjects::Proxy.new(obj, class_methods_namespace)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @return the namespace object that is including the module
|
31
|
+
sig { returns(YARD::CodeObjects::NamespaceObject) }
|
32
|
+
def included_in
|
33
|
+
statement.namespace ? YARD::CodeObjects::Proxy.new(namespace, statement.namespace.source) : namespace
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module YARDSorbet
|
5
|
+
module Handlers
|
6
|
+
# Tracks modules that invoke `mixes_in_class_methods` for use in {IncludeHandler}
|
7
|
+
# @see https://sorbet.org/docs/abstract#interfaces-and-the-included-hook
|
8
|
+
# Sorbet `mixes_in_class_methods` documentation
|
9
|
+
class MixesInClassMethodsHandler < YARD::Handlers::Ruby::Base
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
handles method_call(:mixes_in_class_methods)
|
13
|
+
namespace_only
|
14
|
+
|
15
|
+
sig { void }
|
16
|
+
def process
|
17
|
+
extra_state.mix_in_class_methods ||= {}
|
18
|
+
extra_state.mix_in_class_methods[namespace.to_s] = statement.parameters(false)[0].source
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,100 +1,70 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module YARDSorbet
|
5
|
+
module Handlers
|
6
|
+
# A YARD Handler for Sorbet type declarations
|
7
|
+
class SigHandler < YARD::Handlers::Ruby::Base
|
8
|
+
extend T::Sig
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
handles method_call(:sig)
|
11
|
+
namespace_only
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
prop :params, T::Hash[String, T::Array[String]], default: {}
|
15
|
-
prop :return, T.nilable(T::Array[String])
|
16
|
-
end
|
17
|
-
|
18
|
-
# Skip these node types when parsing `sig` params
|
19
|
-
PARAM_EXCLUDES = T.let(%i[array call hash].freeze, T::Array[Symbol])
|
20
|
-
# Skip these node types when parsing `sig`s
|
21
|
-
SIG_EXCLUDES = T.let(%i[array hash].freeze, T::Array[Symbol])
|
22
|
-
|
23
|
-
private_constant :ParsedSig, :PARAM_EXCLUDES, :SIG_EXCLUDES
|
13
|
+
# These node types attached to sigs represent attr_* declarations
|
14
|
+
ATTR_NODE_TYPES = T.let(%i[command fcall], T::Array[Symbol])
|
15
|
+
private_constant :ATTR_NODE_TYPES
|
24
16
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
parsed_sig.params.each do |name, types|
|
36
|
-
enhance_param(docstring, name, types)
|
17
|
+
# Swap the method definition docstring and the sig docstring.
|
18
|
+
# Parse relevant parts of the `sig` and include them as well.
|
19
|
+
sig { void }
|
20
|
+
def process
|
21
|
+
method_node = NodeUtils.get_method_node(NodeUtils.sibling_node(statement))
|
22
|
+
docstring, directives = Directives.extract_directives(statement.docstring)
|
23
|
+
parse_sig(method_node, docstring)
|
24
|
+
method_node.docstring = docstring.to_raw
|
25
|
+
Directives.add_directives(method_node.docstring, directives)
|
26
|
+
statement.docstring = nil
|
37
27
|
end
|
38
|
-
end
|
39
|
-
method_node.docstring = docstring.to_raw
|
40
|
-
YARDSorbet::Directives.add_directives(method_node.docstring, directives)
|
41
|
-
statement.docstring = nil
|
42
|
-
end
|
43
28
|
|
44
|
-
|
45
|
-
private def enhance_param(docstring, name, types)
|
46
|
-
tag = docstring.tags.find { |t| t.tag_name == 'param' && t.name == name }
|
47
|
-
if tag
|
48
|
-
docstring.delete_tag_if { |t| t == tag }
|
49
|
-
tag.types = types
|
50
|
-
else
|
51
|
-
tag = YARD::Tags::Tag.new(:param, '', types, name)
|
52
|
-
end
|
53
|
-
docstring.add_tag(tag)
|
54
|
-
end
|
29
|
+
private
|
55
30
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
31
|
+
sig { params(method_node: YARD::Parser::Ruby::AstNode, docstring: YARD::Docstring).void }
|
32
|
+
def parse_sig(method_node, docstring)
|
33
|
+
NodeUtils.bfs_traverse(statement) do |n|
|
34
|
+
case n.source
|
35
|
+
when 'abstract'
|
36
|
+
YARDSorbet::TagUtils.upsert_tag(docstring, 'abstract')
|
37
|
+
when 'params'
|
38
|
+
parse_params(method_node, n, docstring)
|
39
|
+
when 'returns', 'void'
|
40
|
+
parse_return(n, docstring)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
60
44
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
docstring.add_tag(tag)
|
71
|
-
end
|
45
|
+
sig do
|
46
|
+
params(
|
47
|
+
method_node: YARD::Parser::Ruby::AstNode,
|
48
|
+
node: YARD::Parser::Ruby::AstNode,
|
49
|
+
docstring: YARD::Docstring
|
50
|
+
).void
|
51
|
+
end
|
52
|
+
def parse_params(method_node, node, docstring)
|
53
|
+
return if ATTR_NODE_TYPES.include?(method_node.type)
|
72
54
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
YARDSorbet::NodeUtils.bfs_traverse(sig_node, exclude: SIG_EXCLUDES) do |n|
|
79
|
-
if n.source == 'abstract'
|
80
|
-
parsed.abstract = true
|
81
|
-
elsif n.source == 'params' && !found_params
|
82
|
-
found_params = true
|
83
|
-
sibling = YARDSorbet::NodeUtils.sibling_node(n)
|
84
|
-
YARDSorbet::NodeUtils.bfs_traverse(sibling, exclude: PARAM_EXCLUDES) do |p|
|
85
|
-
if p.type == :assoc
|
86
|
-
param_name = p.children.first.source[0...-1]
|
87
|
-
types = YARDSorbet::SigToYARD.convert(p.children.last)
|
88
|
-
parsed.params[param_name] = types
|
89
|
-
end
|
55
|
+
sibling = NodeUtils.sibling_node(node)
|
56
|
+
sibling[0][0].each do |p|
|
57
|
+
param_name = p[0][0]
|
58
|
+
types = SigToYARD.convert(p.last)
|
59
|
+
TagUtils.upsert_tag(docstring, 'param', types, param_name)
|
90
60
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
61
|
+
end
|
62
|
+
|
63
|
+
sig { params(node: YARD::Parser::Ruby::AstNode, docstring: YARD::Docstring).void }
|
64
|
+
def parse_return(node, docstring)
|
65
|
+
type = node.source == 'void' ? ['void'] : SigToYARD.convert(NodeUtils.sibling_node(node))
|
66
|
+
TagUtils.upsert_tag(docstring, 'return', type)
|
96
67
|
end
|
97
68
|
end
|
98
|
-
parsed
|
99
69
|
end
|
100
70
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module YARDSorbet
|
5
|
+
module Handlers
|
6
|
+
# Class-level handler that folds all `const` and `prop` declarations into the constructor documentation
|
7
|
+
# this needs to be injected as a module otherwise the default Class handler will overwrite documentation
|
8
|
+
#
|
9
|
+
# @note this module is included in `YARD::Handlers::Ruby::ClassHandler`
|
10
|
+
module StructClassHandler
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { void }
|
14
|
+
def process
|
15
|
+
super
|
16
|
+
return if extra_state.prop_docs.nil?
|
17
|
+
|
18
|
+
# lookup the full YARD path for the current class
|
19
|
+
class_ns = YARD::CodeObjects::ClassObject.new(namespace, statement[0].source.gsub(/\s/, ''))
|
20
|
+
props = extra_state.prop_docs[class_ns]
|
21
|
+
return if props.empty?
|
22
|
+
|
23
|
+
process_t_struct_props(props, class_ns)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Create a virtual `initialize` method with all the `prop`/`const` arguments
|
29
|
+
sig { params(props: T::Array[TStructProp], class_ns: YARD::CodeObjects::ClassObject).void }
|
30
|
+
def process_t_struct_props(props, class_ns)
|
31
|
+
# having the name :initialize & the scope :instance marks this as the constructor.
|
32
|
+
object = YARD::CodeObjects::MethodObject.new(class_ns, :initialize, :instance)
|
33
|
+
# There is a chance that there is a custom initializer, so make sure we steal the existing docstring
|
34
|
+
# and source
|
35
|
+
docstring, directives = Directives.extract_directives(object.docstring)
|
36
|
+
object.tags.each { |tag| docstring.add_tag(tag) }
|
37
|
+
props.each do |prop|
|
38
|
+
TagUtils.upsert_tag(docstring, 'param', prop.types, prop.prop_name, prop.doc)
|
39
|
+
end
|
40
|
+
TagUtils.upsert_tag(docstring, 'return', ['void'])
|
41
|
+
decorate_t_struct_init(object, props, docstring, directives)
|
42
|
+
end
|
43
|
+
|
44
|
+
sig do
|
45
|
+
params(
|
46
|
+
object: YARD::CodeObjects::MethodObject,
|
47
|
+
props: T::Array[TStructProp],
|
48
|
+
docstring: YARD::Docstring,
|
49
|
+
directives: T::Array[String]
|
50
|
+
).void
|
51
|
+
end
|
52
|
+
def decorate_t_struct_init(object, props, docstring, directives)
|
53
|
+
# Use kwarg style arguments, with optionals being marked with a default (unless an actual default was specified)
|
54
|
+
object.parameters = to_object_parameters(props)
|
55
|
+
# The "source" of our constructor is the field declarations
|
56
|
+
object.source ||= props.map(&:source).join("\n")
|
57
|
+
object.docstring = docstring
|
58
|
+
Directives.add_directives(object.docstring, directives)
|
59
|
+
end
|
60
|
+
|
61
|
+
sig { params(props: T::Array[TStructProp]).returns(T::Array[[String, T.nilable(String)]]) }
|
62
|
+
def to_object_parameters(props)
|
63
|
+
props.map do |prop|
|
64
|
+
default = prop.default || (prop.types.include?('nil') ? 'nil' : nil)
|
65
|
+
["#{prop.prop_name}:", default]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
YARD::Handlers::Ruby::ClassHandler.include YARDSorbet::Handlers::StructClassHandler
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module YARDSorbet
|
5
|
+
module Handlers
|
6
|
+
# Handles all `const`/`prop` calls, creating accessor methods, and compiles them for later usage at the class level
|
7
|
+
# in creating a constructor
|
8
|
+
class StructPropHandler < YARD::Handlers::Ruby::Base
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
handles method_call(:const), method_call(:prop)
|
12
|
+
namespace_only
|
13
|
+
|
14
|
+
sig { void }
|
15
|
+
def process
|
16
|
+
name = statement.parameters.first.last.last.source
|
17
|
+
prop = make_prop(name)
|
18
|
+
update_state(prop)
|
19
|
+
object = YARD::CodeObjects::MethodObject.new(namespace, name, scope)
|
20
|
+
decorate_object(object, prop)
|
21
|
+
register_attrs(object, name)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Add the source and docstring to the method object
|
27
|
+
sig { params(object: YARD::CodeObjects::MethodObject, prop: TStructProp).void }
|
28
|
+
def decorate_object(object, prop)
|
29
|
+
object.source = prop.source
|
30
|
+
# TODO: this should use `+` to delimit the attribute name when markdown is disabled
|
31
|
+
reader_docstring = prop.doc.empty? ? "Returns the value of attribute `#{prop.prop_name}`." : prop.doc
|
32
|
+
docstring = YARD::DocstringParser.new.parse(reader_docstring).to_docstring
|
33
|
+
docstring.add_tag(YARD::Tags::Tag.new(:return, '', prop.types))
|
34
|
+
object.docstring = docstring.to_raw
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get the default prop value
|
38
|
+
sig { returns(T.nilable(String)) }
|
39
|
+
def default_value
|
40
|
+
default_node = statement.traverse { |n| break n if n.type == :label && n.source == 'default:' }
|
41
|
+
default_node.parent[1].source if default_node
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { params(name: String).returns(TStructProp) }
|
45
|
+
def make_prop(name)
|
46
|
+
TStructProp.new(
|
47
|
+
default: default_value,
|
48
|
+
doc: statement.docstring.to_s,
|
49
|
+
prop_name: name,
|
50
|
+
source: statement.source,
|
51
|
+
types: SigToYARD.convert(statement.parameters[1])
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Register the field explicitly as an attribute.
|
56
|
+
# While `const` attributes are immutable, `prop` attributes may be reassigned.
|
57
|
+
sig { params(object: YARD::CodeObjects::MethodObject, name: String).void }
|
58
|
+
def register_attrs(object, name)
|
59
|
+
# Create the virtual method in our current scope
|
60
|
+
write = statement.method_name(true) == :prop ? object : nil
|
61
|
+
namespace.attributes[scope][name] ||= SymbolHash[read: object, write: write]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Store the prop for use in the constructor definition
|
65
|
+
sig { params(prop: TStructProp).void }
|
66
|
+
def update_state(prop)
|
67
|
+
extra_state.prop_docs ||= Hash.new { |h, k| h[k] = [] }
|
68
|
+
extra_state.prop_docs[namespace] << prop
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/yard-sorbet/handlers.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
#
|
6
|
-
|
4
|
+
module YARDSorbet
|
5
|
+
# Custom YARD Handlers
|
6
|
+
# @see https://rubydoc.info/gems/yard/YARD/Handlers/Base YARD Base Handler documentation
|
7
|
+
module Handlers; end
|
8
|
+
end
|
7
9
|
|
8
10
|
require_relative 'handlers/abstract_dsl_handler'
|
9
11
|
require_relative 'handlers/enums_handler'
|
12
|
+
require_relative 'handlers/include_handler'
|
13
|
+
require_relative 'handlers/mixes_in_class_methods_handler'
|
10
14
|
require_relative 'handlers/sig_handler'
|
11
|
-
require_relative 'handlers/
|
15
|
+
require_relative 'handlers/struct_class_handler'
|
16
|
+
require_relative 'handlers/struct_prop_handler'
|
@@ -1,68 +1,62 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
T.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
4
|
+
module YARDSorbet
|
5
|
+
# Helper methods for working with `YARD` AST Nodes
|
6
|
+
module NodeUtils
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
# Command node types that can have type signatures
|
10
|
+
ATTRIBUTE_METHODS = T.let(%i[attr attr_accessor attr_reader attr_writer].freeze, T::Array[Symbol])
|
11
|
+
# Node types that can have type signatures
|
12
|
+
SIGABLE_NODE = T.type_alias do
|
13
|
+
T.any(YARD::Parser::Ruby::MethodDefinitionNode, YARD::Parser::Ruby::MethodCallNode)
|
14
|
+
end
|
15
|
+
# Skip these method contents during BFS node traversal, they can have their own nested types via `T.Proc`
|
16
|
+
SKIP_METHOD_CONTENTS = T.let(%i[params returns], T::Array[Symbol])
|
17
|
+
|
18
|
+
private_constant :ATTRIBUTE_METHODS, :SIGABLE_NODE
|
19
|
+
|
20
|
+
# Traverese AST nodes in breadth-first order
|
21
|
+
# @note This will skip over some node types.
|
22
|
+
# @yield [YARD::Parser::Ruby::AstNode]
|
23
|
+
sig do
|
24
|
+
params(
|
25
|
+
node: YARD::Parser::Ruby::AstNode,
|
26
|
+
_blk: T.proc.params(n: YARD::Parser::Ruby::AstNode).void
|
27
|
+
).void
|
28
|
+
end
|
29
|
+
def self.bfs_traverse(node, &_blk)
|
30
|
+
queue = [node]
|
31
|
+
until queue.empty?
|
32
|
+
n = T.must(queue.shift)
|
33
|
+
yield n
|
34
|
+
n.children.each { |c| queue.push(c) }
|
35
|
+
queue.pop if n.is_a?(YARD::Parser::Ruby::MethodCallNode) && SKIP_METHOD_CONTENTS.include?(n.method_name(true))
|
34
36
|
end
|
35
37
|
end
|
36
|
-
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
39
|
+
# Gets the node that a sorbet `sig` can be attached do, bypassing visisbility modifiers and the like
|
40
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(SIGABLE_NODE) }
|
41
|
+
def self.get_method_node(node)
|
42
|
+
case node
|
43
|
+
when YARD::Parser::Ruby::MethodDefinitionNode
|
44
|
+
return node
|
45
|
+
when YARD::Parser::Ruby::MethodCallNode
|
46
|
+
return node if ATTRIBUTE_METHODS.include?(node.method_name(true))
|
47
|
+
end
|
47
48
|
|
48
|
-
|
49
|
-
|
49
|
+
node.jump(:def, :defs)
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
return siblings.fetch(i + 1)
|
52
|
+
# Find and return the adjacent node (ascending)
|
53
|
+
# @raise [IndexError] if the node does not have an adjacent sibling (ascending)
|
54
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(YARD::Parser::Ruby::AstNode) }
|
55
|
+
def self.sibling_node(node)
|
56
|
+
siblings = node.parent.children
|
57
|
+
siblings.each_with_index.find do |sibling, i|
|
58
|
+
return siblings.fetch(i + 1) if sibling.equal?(node)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
62
|
-
|
63
|
-
# Returns true if the given node represents a type signature.
|
64
|
-
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Boolean) }
|
65
|
-
def self.type_signature?(node)
|
66
|
-
node.is_a?(YARD::Parser::Ruby::MethodCallNode) && node.method_name(true) == :sig
|
67
|
-
end
|
68
62
|
end
|
@@ -1,135 +1,130 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# Ruby 2.5 parsed call nodes slightly differently
|
9
|
-
IS_LEGACY_RUBY_VERSION = T.let(RUBY_VERSION.start_with?('2.5.'), T::Boolean)
|
10
|
-
private_constant :IS_LEGACY_RUBY_VERSION
|
11
|
-
|
12
|
-
# @see https://yardoc.org/types.html
|
13
|
-
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
14
|
-
def self.convert(node)
|
15
|
-
types = convert_type(node)
|
16
|
-
# scrub newlines, as they break the YARD parser
|
17
|
-
types.map { |type| type.gsub(/\n\s*/, ' ') }
|
18
|
-
end
|
4
|
+
module YARDSorbet
|
5
|
+
# Translate `sig` type syntax to `YARD` type syntax.
|
6
|
+
module SigToYARD
|
7
|
+
extend T::Sig
|
19
8
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
when :array then handle_array(node)
|
26
|
-
when :call then handle_call(node)
|
27
|
-
when :const_path_ref, :const then handle_const(node)
|
28
|
-
# Fixed hashes as return values are unsupported:
|
29
|
-
# https://github.com/lsegal/yard/issues/425
|
30
|
-
#
|
31
|
-
# Hash key params can be individually documented with `@option`, but
|
32
|
-
# sig translation is unsupported.
|
33
|
-
when :hash, :list then ['Hash']
|
34
|
-
when :var_ref then handle_var_ref(node)
|
35
|
-
# A top-level constant reference, such as ::Klass
|
36
|
-
# It contains a child node of type :const
|
37
|
-
when :top_const_ref then convert(node.children.first)
|
38
|
-
else
|
39
|
-
log.warn("Unsupported sig #{node.type} node #{node.source}")
|
40
|
-
[node.source]
|
9
|
+
# @see https://yardoc.org/types.html
|
10
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
11
|
+
def self.convert(node)
|
12
|
+
# scrub newlines, as they break the YARD parser
|
13
|
+
convert_node(node).map { |type| type.gsub(/\n\s*/, ' ') }
|
41
14
|
end
|
42
|
-
end
|
43
15
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
16
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
17
|
+
private_class_method def self.convert_node(node)
|
18
|
+
case node
|
19
|
+
when YARD::Parser::Ruby::MethodCallNode then convert_call(node)
|
20
|
+
when YARD::Parser::Ruby::ReferenceNode then convert_ref(node)
|
21
|
+
else convert_node_type(node)
|
22
|
+
end
|
23
|
+
end
|
48
24
|
|
49
|
-
|
50
|
-
|
25
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
26
|
+
private_class_method def self.convert_node_type(node)
|
27
|
+
case node.type
|
28
|
+
when :aref then convert_aref(node)
|
29
|
+
when :arg_paren then convert_node(node.first)
|
30
|
+
when :array then convert_array(node)
|
31
|
+
# Fixed hashes as return values are unsupported:
|
32
|
+
# https://github.com/lsegal/yard/issues/425
|
33
|
+
#
|
34
|
+
# Hash key params can be individually documented with `@option`, but
|
35
|
+
# sig translation is currently unsupported.
|
36
|
+
when :hash then ['Hash']
|
37
|
+
# seen when sig methods omit parentheses
|
38
|
+
when :list then convert_list(node)
|
39
|
+
else convert_unknown(node)
|
40
|
+
end
|
41
|
+
end
|
51
42
|
|
52
|
-
|
53
|
-
|
43
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(String) }
|
44
|
+
private_class_method def self.build_generic_type(node)
|
45
|
+
return node.source if node.empty? || node.type != :aref
|
54
46
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
case children.first.source
|
60
|
-
when 'T::Array', 'T::Enumerable', 'T::Range', 'T::Set'
|
61
|
-
collection_type = children.first.source.split('::').last
|
62
|
-
member_type = convert(children.last.children.first).join(', ')
|
63
|
-
["#{collection_type}<#{member_type}>"]
|
64
|
-
when 'T::Hash'
|
65
|
-
kv = children.last.children
|
66
|
-
key_type = convert(kv.first).join(', ')
|
67
|
-
value_type = convert(kv.last).join(', ')
|
68
|
-
["Hash{#{key_type} => #{value_type}}"]
|
69
|
-
else
|
70
|
-
log.info("Unsupported sig aref node #{node.source}")
|
71
|
-
[build_generic_type(node)]
|
47
|
+
collection_type = node.first.source
|
48
|
+
member_type = node.last.children.map { |child| build_generic_type(child) }.join(', ')
|
49
|
+
|
50
|
+
"#{collection_type}[#{member_type}]"
|
72
51
|
end
|
73
|
-
end
|
74
52
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
53
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
54
|
+
private_class_method def self.convert_aref(node)
|
55
|
+
# https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Parametrized_Types
|
56
|
+
case node.first.source
|
57
|
+
when 'T::Array', 'T::Enumerable', 'T::Range', 'T::Set' then convert_collection(node)
|
58
|
+
when 'T::Hash' then convert_hash(node)
|
59
|
+
else
|
60
|
+
log.info("Unsupported sig aref node #{node.source}")
|
61
|
+
[build_generic_type(node)]
|
62
|
+
end
|
63
|
+
end
|
79
64
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
65
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
66
|
+
private_class_method def self.convert_array(node)
|
67
|
+
# https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Order-Dependent_Lists
|
68
|
+
member_types = node.first.children.map { |n| convert_node(n) }
|
69
|
+
sequence = member_types.map { |mt| mt.size == 1 ? mt[0] : mt.to_s.tr('"', '') }.join(', ')
|
70
|
+
["Array(#{sequence})"]
|
71
|
+
end
|
86
72
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
case source
|
91
|
-
# YARD convention for booleans
|
92
|
-
when 'T::Boolean' then ['Boolean']
|
93
|
-
else [source]
|
73
|
+
sig { params(node: YARD::Parser::Ruby::MethodCallNode).returns(T::Array[String]) }
|
74
|
+
private_class_method def self.convert_call(node)
|
75
|
+
node.namespace.source == 'T' ? convert_t_method(node) : [node.source]
|
94
76
|
end
|
95
|
-
end
|
96
77
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
handle_t_method(t_method, node)
|
103
|
-
else
|
104
|
-
[node.source]
|
78
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
79
|
+
private_class_method def self.convert_collection(node)
|
80
|
+
collection_type = node.first.source.split('::').last
|
81
|
+
member_type = convert_node(node.last.first).join(', ')
|
82
|
+
["#{collection_type}<#{member_type}>"]
|
105
83
|
end
|
106
|
-
end
|
107
84
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
[
|
114
|
-
when 'any' then node.children.last.children.first.children.map { |n| convert(n) }.flatten
|
115
|
-
# Order matters here, putting `nil` last results in a more concise
|
116
|
-
# return syntax in the UI (superscripted `?`)
|
117
|
-
when 'nilable' then convert(node.children.last) + ['nil']
|
118
|
-
else
|
119
|
-
log.warn("Unsupported T method #{node.source}")
|
120
|
-
[node.source]
|
85
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
86
|
+
private_class_method def self.convert_hash(node)
|
87
|
+
kv = node.last.children
|
88
|
+
key_type = convert_node(kv.first).join(', ')
|
89
|
+
value_type = convert_node(kv.last).join(', ')
|
90
|
+
["Hash{#{key_type} => #{value_type}}"]
|
121
91
|
end
|
122
|
-
end
|
123
92
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
93
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
94
|
+
private_class_method def self.convert_list(node)
|
95
|
+
node.children.size == 1 ? convert_node(node.children.first) : [node.source]
|
96
|
+
end
|
97
|
+
|
98
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
99
|
+
private_class_method def self.convert_ref(node)
|
100
|
+
source = node.source
|
101
|
+
case source
|
102
|
+
when 'T::Boolean' then ['Boolean'] # YARD convention for booleans
|
103
|
+
# YARD convention is use singleton objects when applicable:
|
104
|
+
# https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Literals
|
105
|
+
when 'FalseClass' then ['false']
|
106
|
+
when 'NilClass' then ['nil']
|
107
|
+
when 'TrueClass' then ['true']
|
108
|
+
else [source]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
sig { params(node: YARD::Parser::Ruby::MethodCallNode).returns(T::Array[String]) }
|
113
|
+
private_class_method def self.convert_t_method(node)
|
114
|
+
case node.method_name(true)
|
115
|
+
when :any then node.last.first.children.map { |n| convert_node(n) }.flatten
|
116
|
+
# Order matters here, putting `nil` last results in a more concise
|
117
|
+
# return syntax in the UI (superscripted `?`)
|
118
|
+
# https://github.com/lsegal/yard/blob/cfa62ae/lib/yard/templates/helpers/html_helper.rb#L499-L500
|
119
|
+
when :nilable then convert_node(node.last) + ['nil']
|
120
|
+
else [node.source]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
125
|
+
private_class_method def self.convert_unknown(node)
|
126
|
+
log.warn("Unsupported sig #{node.type} node #{node.source}")
|
127
|
+
[node.source]
|
133
128
|
end
|
134
129
|
end
|
135
130
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module YARDSorbet
|
5
|
+
# Used to store the details of a `T::Struct` `prop` definition
|
6
|
+
class TStructProp < T::Struct
|
7
|
+
const :default, T.nilable(String)
|
8
|
+
const :doc, String
|
9
|
+
const :prop_name, String
|
10
|
+
const :source, String
|
11
|
+
const :types, T::Array[String]
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module YARDSorbet
|
5
|
+
# Helper methods for working with `YARD` tags
|
6
|
+
module TagUtils
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
# @return the tag with the matching `tag_name` and `name`, or `nil`
|
10
|
+
sig do
|
11
|
+
params(docstring: YARD::Docstring, tag_name: String, name: T.nilable(String))
|
12
|
+
.returns(T.nilable(YARD::Tags::Tag))
|
13
|
+
end
|
14
|
+
def self.find_tag(docstring, tag_name, name)
|
15
|
+
docstring.tags.find { |t| t.tag_name == tag_name && t.name == name }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create or update a `YARD` tag with type information
|
19
|
+
sig do
|
20
|
+
params(
|
21
|
+
docstring: YARD::Docstring,
|
22
|
+
tag_name: String,
|
23
|
+
types: T.nilable(T::Array[String]),
|
24
|
+
name: T.nilable(String),
|
25
|
+
text: String
|
26
|
+
).void
|
27
|
+
end
|
28
|
+
def self.upsert_tag(docstring, tag_name, types = nil, name = nil, text = '')
|
29
|
+
tag = find_tag(docstring, tag_name, name)
|
30
|
+
if tag
|
31
|
+
return unless types
|
32
|
+
|
33
|
+
# Updating a tag in place doesn't seem to work, so we'll delete it, add the types, and re-add it
|
34
|
+
docstring.delete_tag_if { |t| t == tag }
|
35
|
+
# overwrite any existing type annotation (sigs should win)
|
36
|
+
tag.types = types
|
37
|
+
tag.text = text unless text.empty?
|
38
|
+
else
|
39
|
+
tag = YARD::Tags::Tag.new(tag_name, text, types, name)
|
40
|
+
end
|
41
|
+
docstring.add_tag(tag)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/yard-sorbet/version.rb
CHANGED
data/lib/yard-sorbet.rb
CHANGED
@@ -4,11 +4,11 @@
|
|
4
4
|
require 'sorbet-runtime'
|
5
5
|
require 'yard'
|
6
6
|
|
7
|
-
|
8
|
-
module YARDSorbet; end
|
7
|
+
require_relative 'yard-sorbet/version'
|
9
8
|
|
10
9
|
require_relative 'yard-sorbet/directives'
|
11
10
|
require_relative 'yard-sorbet/handlers'
|
12
11
|
require_relative 'yard-sorbet/node_utils'
|
13
12
|
require_relative 'yard-sorbet/sig_to_yard'
|
14
|
-
require_relative 'yard-sorbet/
|
13
|
+
require_relative 'yard-sorbet/t_struct_prop'
|
14
|
+
require_relative 'yard-sorbet/tag_utils'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yard-sorbet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Douglas Eichelberger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler-audit
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: redcarpet
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.5'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.5'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rspec
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +86,14 @@ dependencies:
|
|
72
86
|
requirements:
|
73
87
|
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: 1.
|
89
|
+
version: 1.22.0
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.
|
96
|
+
version: 1.22.0
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rubocop-performance
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,28 +114,28 @@ dependencies:
|
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.
|
117
|
+
version: 0.6.0
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
124
|
+
version: 0.6.0
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: rubocop-rspec
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
129
|
- - "~>"
|
116
130
|
- !ruby/object:Gem::Version
|
117
|
-
version: 2.
|
131
|
+
version: 2.5.0
|
118
132
|
type: :development
|
119
133
|
prerelease: false
|
120
134
|
version_requirements: !ruby/object:Gem::Requirement
|
121
135
|
requirements:
|
122
136
|
- - "~>"
|
123
137
|
- !ruby/object:Gem::Version
|
124
|
-
version: 2.
|
138
|
+
version: 2.5.0
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: rubocop-sorbet
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,42 +170,42 @@ dependencies:
|
|
156
170
|
requirements:
|
157
171
|
- - "~>"
|
158
172
|
- !ruby/object:Gem::Version
|
159
|
-
version: 0.5.
|
173
|
+
version: 0.5.9204
|
160
174
|
type: :development
|
161
175
|
prerelease: false
|
162
176
|
version_requirements: !ruby/object:Gem::Requirement
|
163
177
|
requirements:
|
164
178
|
- - "~>"
|
165
179
|
- !ruby/object:Gem::Version
|
166
|
-
version: 0.5.
|
180
|
+
version: 0.5.9204
|
167
181
|
- !ruby/object:Gem::Dependency
|
168
182
|
name: sorbet-runtime
|
169
183
|
requirement: !ruby/object:Gem::Requirement
|
170
184
|
requirements:
|
171
185
|
- - ">="
|
172
186
|
- !ruby/object:Gem::Version
|
173
|
-
version: 0.5
|
187
|
+
version: '0.5'
|
174
188
|
type: :runtime
|
175
189
|
prerelease: false
|
176
190
|
version_requirements: !ruby/object:Gem::Requirement
|
177
191
|
requirements:
|
178
192
|
- - ">="
|
179
193
|
- !ruby/object:Gem::Version
|
180
|
-
version: 0.5
|
194
|
+
version: '0.5'
|
181
195
|
- !ruby/object:Gem::Dependency
|
182
196
|
name: yard
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|
184
198
|
requirements:
|
185
199
|
- - ">="
|
186
200
|
- !ruby/object:Gem::Version
|
187
|
-
version: 0.9
|
201
|
+
version: '0.9'
|
188
202
|
type: :runtime
|
189
203
|
prerelease: false
|
190
204
|
version_requirements: !ruby/object:Gem::Requirement
|
191
205
|
requirements:
|
192
206
|
- - ">="
|
193
207
|
- !ruby/object:Gem::Version
|
194
|
-
version: 0.9
|
208
|
+
version: '0.9'
|
195
209
|
description: 'A YARD plugin that incorporates Sorbet type information
|
196
210
|
|
197
211
|
'
|
@@ -206,17 +220,23 @@ files:
|
|
206
220
|
- lib/yard-sorbet/handlers.rb
|
207
221
|
- lib/yard-sorbet/handlers/abstract_dsl_handler.rb
|
208
222
|
- lib/yard-sorbet/handlers/enums_handler.rb
|
223
|
+
- lib/yard-sorbet/handlers/include_handler.rb
|
224
|
+
- lib/yard-sorbet/handlers/mixes_in_class_methods_handler.rb
|
209
225
|
- lib/yard-sorbet/handlers/sig_handler.rb
|
210
|
-
- lib/yard-sorbet/handlers/
|
226
|
+
- lib/yard-sorbet/handlers/struct_class_handler.rb
|
227
|
+
- lib/yard-sorbet/handlers/struct_class_handler.rbi
|
228
|
+
- lib/yard-sorbet/handlers/struct_prop_handler.rb
|
211
229
|
- lib/yard-sorbet/node_utils.rb
|
212
230
|
- lib/yard-sorbet/sig_to_yard.rb
|
231
|
+
- lib/yard-sorbet/t_struct_prop.rb
|
232
|
+
- lib/yard-sorbet/tag_utils.rb
|
213
233
|
- lib/yard-sorbet/version.rb
|
214
234
|
homepage: https://github.com/dduugg/yard-sorbet
|
215
235
|
licenses:
|
216
236
|
- Apache-2.0
|
217
237
|
metadata:
|
218
238
|
bug_tracker_uri: https://github.com/dduugg/yard-sorbet/issues
|
219
|
-
changelog_uri: https://github.com/dduugg/yard-sorbet/blob/
|
239
|
+
changelog_uri: https://github.com/dduugg/yard-sorbet/blob/main/CHANGELOG.md
|
220
240
|
documentation_uri: https://dduugg.github.io/yard-sorbet/
|
221
241
|
homepage_uri: https://github.com/dduugg/yard-sorbet
|
222
242
|
source_code_uri: https://github.com/dduugg/yard-sorbet
|
@@ -236,7 +256,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
236
256
|
- !ruby/object:Gem::Version
|
237
257
|
version: '0'
|
238
258
|
requirements: []
|
239
|
-
rubygems_version: 3.1.
|
259
|
+
rubygems_version: 3.1.6
|
240
260
|
signing_key:
|
241
261
|
specification_version: 4
|
242
262
|
summary: Create YARD docs from Sorbet type signatures
|
@@ -1,109 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# Handles all +const+/+prop+ calls, creating accessor methods, and compiles them for later usage at the class level
|
5
|
-
# in creating a constructor
|
6
|
-
class YARDSorbet::Handlers::StructHandler < YARD::Handlers::Ruby::Base
|
7
|
-
extend T::Sig
|
8
|
-
|
9
|
-
handles method_call(:const), method_call(:prop)
|
10
|
-
namespace_only
|
11
|
-
|
12
|
-
sig { void }
|
13
|
-
def process
|
14
|
-
# Store the property for use in the constructor definition
|
15
|
-
name = statement.parameters[0].jump(
|
16
|
-
:ident, # handles most "normal" identifiers
|
17
|
-
:kw, # handles prop names using reserved words like `end` or `def`
|
18
|
-
:const # handles capitalized prop names like Foo
|
19
|
-
).source
|
20
|
-
|
21
|
-
doc = statement.docstring.to_s
|
22
|
-
source = statement.source
|
23
|
-
types = YARDSorbet::SigToYARD.convert(statement.parameters[1])
|
24
|
-
default_node = statement.traverse { |n| break n if n.source == 'default:' && n.type == :label }
|
25
|
-
default = default_node.parent[1].source if default_node
|
26
|
-
|
27
|
-
extra_state.prop_docs ||= Hash.new { |h, k| h[k] = [] }
|
28
|
-
extra_state.prop_docs[namespace] << {
|
29
|
-
doc: doc,
|
30
|
-
prop_name: name,
|
31
|
-
types: types,
|
32
|
-
source: source,
|
33
|
-
default: default
|
34
|
-
}
|
35
|
-
|
36
|
-
# Create the virtual method in our current scope
|
37
|
-
namespace.attributes[scope][name] ||= SymbolHash[read: nil, write: nil]
|
38
|
-
|
39
|
-
object = MethodObject.new(namespace, name, scope)
|
40
|
-
object.source = source
|
41
|
-
|
42
|
-
reader_docstring = doc.empty? ? "Returns the value of attribute +#{name}+." : doc
|
43
|
-
docstring = YARD::DocstringParser.new.parse(reader_docstring).to_docstring
|
44
|
-
docstring.add_tag(YARD::Tags::Tag.new(:return, '', types))
|
45
|
-
object.docstring = docstring.to_raw
|
46
|
-
|
47
|
-
# Register the object explicitly as an attribute.
|
48
|
-
# While `const` attributes are immutable, `prop` attributes may be reassigned.
|
49
|
-
if statement.method_name.source == 'prop'
|
50
|
-
namespace.attributes[scope][name][:write] = object
|
51
|
-
end
|
52
|
-
namespace.attributes[scope][name][:read] = object
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# Class-level handler that folds all +const+ and +prop+ declarations into the constructor documentation
|
57
|
-
# this needs to be injected as a module otherwise the default Class handler will overwrite documentation
|
58
|
-
module YARDSorbet::StructClassHandler
|
59
|
-
extend T::Helpers
|
60
|
-
extend T::Sig
|
61
|
-
|
62
|
-
requires_ancestor YARD::Handlers::Ruby::ClassHandler
|
63
|
-
|
64
|
-
sig { void }
|
65
|
-
def process
|
66
|
-
super
|
67
|
-
|
68
|
-
return if extra_state.prop_docs.nil?
|
69
|
-
|
70
|
-
# lookup the full YARD path for the current class
|
71
|
-
class_ns = YARD::CodeObjects::ClassObject.new(namespace, statement[0].source.gsub(/\s/, ''))
|
72
|
-
props = extra_state.prop_docs[class_ns]
|
73
|
-
|
74
|
-
return if props.empty?
|
75
|
-
|
76
|
-
# Create a virtual `initialize` method with all the `prop`/`const` arguments
|
77
|
-
# having the name :initialize & the scope :instance marks this as the constructor.
|
78
|
-
# There is a chance that there is a custom initializer, so make sure we steal the existing docstring
|
79
|
-
# and source
|
80
|
-
object = YARD::CodeObjects::MethodObject.new(class_ns, :initialize, :instance)
|
81
|
-
|
82
|
-
docstring, directives = YARDSorbet::Directives.extract_directives(object.docstring || '')
|
83
|
-
|
84
|
-
# Annotate the parameters of the constructor with the prop docs
|
85
|
-
props.each do |prop|
|
86
|
-
docstring.add_tag(YARD::Tags::Tag.new(:param, prop[:doc], prop[:types], prop[:prop_name]))
|
87
|
-
end
|
88
|
-
|
89
|
-
docstring.add_tag(YARD::Tags::Tag.new(:return, '', class_ns))
|
90
|
-
|
91
|
-
# Use kwarg style arguments, with optionals being marked with a default (unless an actual default was specified)
|
92
|
-
object.parameters = props.map do |prop|
|
93
|
-
default = prop[:default] || (prop[:types].include?('nil') ? 'nil' : nil)
|
94
|
-
["#{prop[:prop_name]}:", default]
|
95
|
-
end
|
96
|
-
|
97
|
-
# The "source" of our constructor is compromised with the props/consts
|
98
|
-
object.source ||= props.map { |p| p[:source] }.join("\n")
|
99
|
-
object.explicit ||= false # not strictly necessary
|
100
|
-
|
101
|
-
register(object)
|
102
|
-
|
103
|
-
object.docstring = docstring.to_raw
|
104
|
-
|
105
|
-
YARDSorbet::Directives.add_directives(object.docstring, directives)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
YARD::Handlers::Ruby::ClassHandler.include YARDSorbet::StructClassHandler
|