yard-sorbet 0.1.0 → 0.5.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.rb +3 -2
- data/lib/yard-sorbet/directives.rb +5 -1
- data/lib/yard-sorbet/handlers.rb +11 -0
- data/lib/yard-sorbet/handlers/abstract_dsl_handler.rb +26 -0
- data/lib/yard-sorbet/handlers/enums_handler.rb +28 -0
- data/lib/yard-sorbet/handlers/sig_handler.rb +100 -0
- data/lib/yard-sorbet/{struct_handler.rb → handlers/struct_handler.rb} +28 -12
- data/lib/yard-sorbet/node_utils.rb +68 -0
- data/lib/yard-sorbet/sig_to_yard.rb +112 -83
- data/lib/yard-sorbet/version.rb +2 -1
- metadata +61 -37
- data/.editorconfig +0 -9
- data/.gitignore +0 -40
- data/.rspec +0 -3
- data/.rubocop.yml +0 -62
- data/.travis.yml +0 -8
- data/.yardopts +0 -2
- data/CHANGELOG.md +0 -30
- data/Gemfile +0 -8
- data/README.md +0 -17
- data/Rakefile +0 -19
- data/lib/yard-sorbet/sig_handler.rb +0 -147
- data/yard-sorbet.gemspec +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 360c73bcd93390b21ebf0206cce339822368fca005d03fa3d08a4444a3cab367
|
4
|
+
data.tar.gz: 7b03b59846235fd395bca99868561eaa299b7dbe21eea7c91307c602236baec9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 364ee6ff02687a3a4e2ffd3384c2eb68a848afe60ad0e94c8d675df81ae9a820e805ed07ce1537cee59eb3e979ce2488a1a2210aa19cc1b40365b9dc97368864
|
7
|
+
data.tar.gz: f26eb01fcad908815f27afc9a42ad72050625e5279e3b3b7576991aa1b14ba685148c12d74bbc2cdba19e69b1f416f48fa89253d34a5635259794d088ad7415f
|
data/lib/yard-sorbet.rb
CHANGED
@@ -8,6 +8,7 @@ require 'yard'
|
|
8
8
|
module YARDSorbet; end
|
9
9
|
|
10
10
|
require_relative 'yard-sorbet/directives'
|
11
|
-
require_relative 'yard-sorbet/
|
11
|
+
require_relative 'yard-sorbet/handlers'
|
12
|
+
require_relative 'yard-sorbet/node_utils'
|
12
13
|
require_relative 'yard-sorbet/sig_to_yard'
|
13
|
-
require_relative 'yard-sorbet/
|
14
|
+
require_relative 'yard-sorbet/version'
|
@@ -1,8 +1,11 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# Extract & re-add directives to a docstring
|
5
5
|
module YARDSorbet::Directives
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { params(docstring: T.nilable(String)).returns([YARD::Docstring, T::Array[String]]) }
|
6
9
|
def self.extract_directives(docstring)
|
7
10
|
parser = YARD::DocstringParser.new.parse(docstring)
|
8
11
|
# Directives are already parsed at this point, and there doesn't
|
@@ -16,6 +19,7 @@ module YARDSorbet::Directives
|
|
16
19
|
[parser.to_docstring, directives]
|
17
20
|
end
|
18
21
|
|
22
|
+
sig { params(docstring: String, directives: T::Array[String]).void }
|
19
23
|
def self.add_directives(docstring, directives)
|
20
24
|
directives.each do |directive|
|
21
25
|
docstring.concat("\n#{directive}")
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Custom YARD Handlers
|
5
|
+
# @see https://rubydoc.info/gems/yard/YARD/Handlers/Base YARD Base Handler documentation
|
6
|
+
module YARDSorbet::Handlers; end
|
7
|
+
|
8
|
+
require_relative 'handlers/abstract_dsl_handler'
|
9
|
+
require_relative 'handlers/enums_handler'
|
10
|
+
require_relative 'handlers/sig_handler'
|
11
|
+
require_relative 'handlers/struct_handler'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Apllies an +@abstract+ tag to +abstract!+/+interface!+ modules (if not alerady present).
|
5
|
+
class YARDSorbet::Handlers::AbstractDSLHandler < YARD::Handlers::Ruby::Base
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
handles method_call(:abstract!), method_call(:interface!)
|
9
|
+
namespace_only
|
10
|
+
|
11
|
+
# The text accompanying the `@abstract` tag.
|
12
|
+
# @see https://github.com/lsegal/yard/blob/main/templates/default/docstring/html/abstract.erb
|
13
|
+
# The `@abstract` tag template
|
14
|
+
TAG_TEXT = 'Subclasses must implement the `abstract` methods below.'
|
15
|
+
# Extra text for class namespaces
|
16
|
+
CLASS_TAG_TEXT = T.let("It cannont be directly instantiated. #{TAG_TEXT}", String)
|
17
|
+
|
18
|
+
sig { void }
|
19
|
+
def process
|
20
|
+
return if namespace.has_tag?(:abstract)
|
21
|
+
|
22
|
+
text = namespace.is_a?(YARD::CodeObjects::ClassObject) ? CLASS_TAG_TEXT : TAG_TEXT
|
23
|
+
tag = YARD::Tags::Tag.new(:abstract, text)
|
24
|
+
namespace.add_tag(tag)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Handle +enums+ calls, registering enum values as constants
|
5
|
+
class YARDSorbet::Handlers::EnumsHandler < YARD::Handlers::Ruby::Base
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
handles method_call(:enums)
|
9
|
+
namespace_only
|
10
|
+
|
11
|
+
sig { void }
|
12
|
+
def process
|
13
|
+
statement.traverse do |node|
|
14
|
+
if const_assign_node?(node)
|
15
|
+
register YARD::CodeObjects::ConstantObject.new(namespace, node.first.source) do |obj|
|
16
|
+
obj.docstring = node.docstring
|
17
|
+
obj.source = node
|
18
|
+
obj.value = node.last.source
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Boolean) }
|
25
|
+
private def const_assign_node?(node)
|
26
|
+
node.type == :assign && node.children.first.children.first.type == :const
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# A YARD Handler for Sorbet type declarations
|
5
|
+
class YARDSorbet::Handlers::SigHandler < YARD::Handlers::Ruby::Base
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
handles method_call(:sig)
|
9
|
+
namespace_only
|
10
|
+
|
11
|
+
# A struct that holds the parsed contents of a Sorbet type signature
|
12
|
+
class ParsedSig < T::Struct
|
13
|
+
prop :abstract, T::Boolean, default: false
|
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
|
24
|
+
|
25
|
+
# Swap the method definition docstring and the sig docstring.
|
26
|
+
# Parse relevant parts of the +sig+ and include them as well.
|
27
|
+
sig { void }
|
28
|
+
def process
|
29
|
+
method_node = YARDSorbet::NodeUtils.get_method_node(YARDSorbet::NodeUtils.sibling_node(statement))
|
30
|
+
docstring, directives = YARDSorbet::Directives.extract_directives(statement.docstring)
|
31
|
+
parsed_sig = parse_sig(statement)
|
32
|
+
enhance_tag(docstring, :abstract, parsed_sig)
|
33
|
+
enhance_tag(docstring, :return, parsed_sig)
|
34
|
+
if method_node.type != :command
|
35
|
+
parsed_sig.params.each do |name, types|
|
36
|
+
enhance_param(docstring, name, types)
|
37
|
+
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
|
+
|
44
|
+
sig { params(docstring: YARD::Docstring, name: String, types: T::Array[String]).void }
|
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
|
55
|
+
|
56
|
+
sig { params(docstring: YARD::Docstring, type: Symbol, parsed_sig: ParsedSig).void }
|
57
|
+
private def enhance_tag(docstring, type, parsed_sig)
|
58
|
+
type_value = parsed_sig.public_send(type)
|
59
|
+
return unless type_value
|
60
|
+
|
61
|
+
tag = docstring.tags.find { |t| t.tag_name == type.to_s }
|
62
|
+
if tag
|
63
|
+
docstring.delete_tags(type)
|
64
|
+
else
|
65
|
+
tag = YARD::Tags::Tag.new(type, '')
|
66
|
+
end
|
67
|
+
if type_value.is_a?(Array)
|
68
|
+
tag.types = type_value
|
69
|
+
end
|
70
|
+
docstring.add_tag(tag)
|
71
|
+
end
|
72
|
+
|
73
|
+
sig { params(sig_node: YARD::Parser::Ruby::MethodCallNode).returns(ParsedSig) }
|
74
|
+
private def parse_sig(sig_node)
|
75
|
+
parsed = ParsedSig.new
|
76
|
+
found_params = T.let(false, T::Boolean)
|
77
|
+
found_return = T.let(false, T::Boolean)
|
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
|
90
|
+
end
|
91
|
+
elsif n.source == 'returns' && !found_return
|
92
|
+
found_return = true
|
93
|
+
parsed.return = YARDSorbet::SigToYARD.convert(YARDSorbet::NodeUtils.sibling_node(n))
|
94
|
+
elsif n.source == 'void'
|
95
|
+
parsed.return ||= ['void']
|
96
|
+
end
|
97
|
+
end
|
98
|
+
parsed
|
99
|
+
end
|
100
|
+
end
|
@@ -1,15 +1,23 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
#
|
4
|
+
# Handles all +const+/+prop+ calls, creating accessor methods, and compiles them for later usage at the class level
|
5
5
|
# in creating a constructor
|
6
|
-
class YARDSorbet::StructHandler < YARD::Handlers::Ruby::Base
|
7
|
-
|
6
|
+
class YARDSorbet::Handlers::StructHandler < YARD::Handlers::Ruby::Base
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
handles method_call(:const), method_call(:prop)
|
8
10
|
namespace_only
|
9
11
|
|
12
|
+
sig { void }
|
10
13
|
def process
|
11
14
|
# Store the property for use in the constructor definition
|
12
|
-
name = statement.parameters[0].jump(
|
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
|
+
|
13
21
|
doc = statement.docstring.to_s
|
14
22
|
source = statement.source
|
15
23
|
types = YARDSorbet::SigToYARD.convert(statement.parameters[1])
|
@@ -36,24 +44,34 @@ class YARDSorbet::StructHandler < YARD::Handlers::Ruby::Base
|
|
36
44
|
docstring.add_tag(YARD::Tags::Tag.new(:return, '', types))
|
37
45
|
object.docstring = docstring.to_raw
|
38
46
|
|
39
|
-
# Register the object explicitly as an attribute
|
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
|
40
52
|
namespace.attributes[scope][name][:read] = object
|
41
53
|
end
|
42
54
|
end
|
43
55
|
|
44
|
-
# Class-level handler that folds all
|
56
|
+
# Class-level handler that folds all +const+ and +prop+ declarations into the constructor documentation
|
45
57
|
# this needs to be injected as a module otherwise the default Class handler will overwrite documentation
|
46
58
|
module YARDSorbet::StructClassHandler
|
59
|
+
extend T::Helpers
|
60
|
+
extend T::Sig
|
61
|
+
|
62
|
+
requires_ancestor YARD::Handlers::Ruby::ClassHandler
|
63
|
+
|
64
|
+
sig { void }
|
47
65
|
def process
|
48
|
-
|
66
|
+
super
|
49
67
|
|
50
|
-
return
|
68
|
+
return if extra_state.prop_docs.nil?
|
51
69
|
|
52
70
|
# lookup the full YARD path for the current class
|
53
71
|
class_ns = YARD::CodeObjects::ClassObject.new(namespace, statement[0].source.gsub(/\s/, ''))
|
54
72
|
props = extra_state.prop_docs[class_ns]
|
55
73
|
|
56
|
-
return
|
74
|
+
return if props.empty?
|
57
75
|
|
58
76
|
# Create a virtual `initialize` method with all the `prop`/`const` arguments
|
59
77
|
# having the name :initialize & the scope :instance marks this as the constructor.
|
@@ -85,8 +103,6 @@ module YARDSorbet::StructClassHandler
|
|
85
103
|
object.docstring = docstring.to_raw
|
86
104
|
|
87
105
|
YARDSorbet::Directives.add_directives(object.docstring, directives)
|
88
|
-
|
89
|
-
ret
|
90
106
|
end
|
91
107
|
end
|
92
108
|
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Helper methods for working with YARD AST Nodes
|
5
|
+
module YARDSorbet::NodeUtils
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
# Command node types that can have type signatures
|
9
|
+
ATTRIBUTE_METHODS = T.let(%i[attr attr_accessor attr_reader attr_writer].freeze, T::Array[Symbol])
|
10
|
+
# Node types that can have type signatures
|
11
|
+
SIGABLE_NODE = T.type_alias do
|
12
|
+
T.any(YARD::Parser::Ruby::MethodDefinitionNode, YARD::Parser::Ruby::MethodCallNode)
|
13
|
+
end
|
14
|
+
|
15
|
+
private_constant :ATTRIBUTE_METHODS, :SIGABLE_NODE
|
16
|
+
|
17
|
+
# @yield [YARD::Parser::Ruby::AstNode]
|
18
|
+
sig do
|
19
|
+
params(
|
20
|
+
node: YARD::Parser::Ruby::AstNode,
|
21
|
+
exclude: T::Array[Symbol],
|
22
|
+
_blk: T.proc.params(n: YARD::Parser::Ruby::AstNode).void
|
23
|
+
).void
|
24
|
+
end
|
25
|
+
def self.bfs_traverse(node, exclude: [], &_blk)
|
26
|
+
queue = [node]
|
27
|
+
until queue.empty?
|
28
|
+
n = T.must(queue.shift)
|
29
|
+
yield n
|
30
|
+
n.children.each do |c|
|
31
|
+
unless exclude.include?(c.type)
|
32
|
+
queue.push(c)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Gets the node that a sorbet `sig` can be attached do, bypassing visisbility modifiers and the like
|
39
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(SIGABLE_NODE) }
|
40
|
+
def self.get_method_node(node)
|
41
|
+
case node
|
42
|
+
when YARD::Parser::Ruby::MethodDefinitionNode
|
43
|
+
return node
|
44
|
+
when YARD::Parser::Ruby::MethodCallNode
|
45
|
+
return node if ATTRIBUTE_METHODS.include?(node.method_name(true))
|
46
|
+
end
|
47
|
+
|
48
|
+
node.jump(:def, :defs)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Find and return the adjacent node (ascending)
|
52
|
+
# @raise [IndexError] if the node does not have an adjacent sibling (ascending)
|
53
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(YARD::Parser::Ruby::AstNode) }
|
54
|
+
def self.sibling_node(node)
|
55
|
+
siblings = node.parent.children
|
56
|
+
siblings.each_with_index.find do |sibling, i|
|
57
|
+
if sibling.equal?(node)
|
58
|
+
return siblings.fetch(i + 1)
|
59
|
+
end
|
60
|
+
end
|
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
|
+
end
|
@@ -1,106 +1,135 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
# Translate sig type syntax to YARD type syntax.
|
4
|
+
# Translate +sig+ type syntax to +YARD+ type syntax.
|
5
5
|
module YARDSorbet::SigToYARD
|
6
|
-
|
6
|
+
extend T::Sig
|
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
|
7
11
|
|
8
12
|
# @see https://yardoc.org/types.html
|
13
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
9
14
|
def self.convert(node)
|
10
15
|
types = convert_type(node)
|
11
16
|
# scrub newlines, as they break the YARD parser
|
12
17
|
types.map { |type| type.gsub(/\n\s*/, ' ') }
|
13
18
|
end
|
14
19
|
|
15
|
-
|
16
|
-
|
20
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
21
|
+
private_class_method def self.convert_type(node)
|
17
22
|
case node.type
|
18
|
-
when :aref
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
when :arg_paren
|
34
|
-
convert(children.first.children.first)
|
35
|
-
when :array
|
36
|
-
# https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Order-Dependent_Lists
|
37
|
-
member_types = children.first.children.map { |n| convert(n) }.join(', ')
|
38
|
-
["Array(#{member_types})"]
|
39
|
-
when :call
|
40
|
-
if children[0].source == 'T'
|
41
|
-
t_method = IS_LEGACY_RUBY_VERSION ? children[1].source : children[2].source
|
42
|
-
case t_method
|
43
|
-
when 'all', 'attached_class', 'class_of', 'enum', 'noreturn', 'self_type', 'type_parameter', 'untyped'
|
44
|
-
# YARD doesn't have equivalent notions, so we just use the raw source
|
45
|
-
[node.source]
|
46
|
-
when 'any'
|
47
|
-
children.last.children.first.children.map { |n| convert(n) }.flatten
|
48
|
-
when 'nilable'
|
49
|
-
# Order matters here, putting `nil` last results in a more concise
|
50
|
-
# return syntax in the UI (superscripted `?`)
|
51
|
-
convert(children.last) + ['nil']
|
52
|
-
else
|
53
|
-
log.warn("Unsupported T method #{node.source}")
|
54
|
-
[node.source]
|
55
|
-
end
|
56
|
-
else
|
57
|
-
[node.source]
|
58
|
-
end
|
59
|
-
when :const_path_ref, :const
|
60
|
-
case node.source
|
61
|
-
when 'T::Boolean'
|
62
|
-
['Boolean'] # YARD convention for booleans
|
63
|
-
else
|
64
|
-
[node.source]
|
65
|
-
end
|
66
|
-
when :hash, :list
|
67
|
-
# Fixed hashes as return values are unsupported:
|
68
|
-
# https://github.com/lsegal/yard/issues/425
|
69
|
-
#
|
70
|
-
# Hash key params can be individually documented with `@option`, but
|
71
|
-
# sig translation is unsupported.
|
72
|
-
['Hash']
|
73
|
-
when :var_ref
|
74
|
-
# YARD convention is use singleton objects when applicable:
|
75
|
-
# https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Literals
|
76
|
-
case node.source
|
77
|
-
when 'FalseClass'
|
78
|
-
['false']
|
79
|
-
when 'NilClass'
|
80
|
-
['nil']
|
81
|
-
when 'TrueClass'
|
82
|
-
['true']
|
83
|
-
else
|
84
|
-
[node.source]
|
85
|
-
end
|
86
|
-
when :top_const_ref
|
87
|
-
# A top-level constant reference, such as ::Klass
|
88
|
-
# It contains a child node of type :const
|
89
|
-
convert(children.first)
|
23
|
+
when :aref then handle_aref(node)
|
24
|
+
when :arg_paren then handle_arg_paren(node)
|
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)
|
90
38
|
else
|
91
39
|
log.warn("Unsupported sig #{node.type} node #{node.source}")
|
92
40
|
[node.source]
|
93
41
|
end
|
94
42
|
end
|
95
43
|
|
96
|
-
|
97
|
-
|
44
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(String) }
|
45
|
+
private_class_method def self.build_generic_type(node)
|
46
|
+
children = node.children
|
47
|
+
return node.source if children.empty? || node.type != :aref
|
98
48
|
|
99
|
-
collection_type =
|
100
|
-
member_type =
|
101
|
-
.map { |child| build_generic_type(child) }
|
102
|
-
.join(', ')
|
49
|
+
collection_type = children.first.source
|
50
|
+
member_type = children.last.children.map { |child| build_generic_type(child) }.join(', ')
|
103
51
|
|
104
52
|
"#{collection_type}[#{member_type}]"
|
105
53
|
end
|
54
|
+
|
55
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
56
|
+
private_class_method def self.handle_aref(node)
|
57
|
+
children = node.children
|
58
|
+
# https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Parametrized_Types
|
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)]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
76
|
+
private_class_method def self.handle_arg_paren(node)
|
77
|
+
convert(node.children.first.children.first)
|
78
|
+
end
|
79
|
+
|
80
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
81
|
+
private_class_method def self.handle_array(node)
|
82
|
+
# https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Order-Dependent_Lists
|
83
|
+
member_types = node.children.first.children.map { |n| convert(n) }.join(', ')
|
84
|
+
["Array(#{member_types})"]
|
85
|
+
end
|
86
|
+
|
87
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
88
|
+
private_class_method def self.handle_const(node)
|
89
|
+
source = node.source
|
90
|
+
case source
|
91
|
+
# YARD convention for booleans
|
92
|
+
when 'T::Boolean' then ['Boolean']
|
93
|
+
else [source]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
98
|
+
private_class_method def self.handle_call(node)
|
99
|
+
children = node.children
|
100
|
+
if children[0].source == 'T'
|
101
|
+
t_method = IS_LEGACY_RUBY_VERSION ? children[1].source : children[2].source
|
102
|
+
handle_t_method(t_method, node)
|
103
|
+
else
|
104
|
+
[node.source]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
sig { params(method_name: String, node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
109
|
+
private_class_method def self.handle_t_method(method_name, node)
|
110
|
+
case method_name
|
111
|
+
# YARD doesn't have equivalent notions, so we just use the raw source
|
112
|
+
when 'all', 'attached_class', 'class_of', 'enum', 'noreturn', 'self_type', 'type_parameter', 'untyped'
|
113
|
+
[node.source]
|
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]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
sig { params(node: YARD::Parser::Ruby::AstNode).returns(T::Array[String]) }
|
125
|
+
private_class_method def self.handle_var_ref(node)
|
126
|
+
# YARD convention is use singleton objects when applicable:
|
127
|
+
# https://www.rubydoc.info/gems/yard/file/docs/Tags.md#Literals
|
128
|
+
case node.source
|
129
|
+
when 'FalseClass' then ['false']
|
130
|
+
when 'NilClass' then ['nil']
|
131
|
+
when 'TrueClass' then ['true']
|
132
|
+
else [node.source]
|
133
|
+
end
|
134
|
+
end
|
106
135
|
end
|
data/lib/yard-sorbet/version.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yard-sorbet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.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:
|
11
|
+
date: 2021-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler-audit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: codecov
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
|
-
- - "
|
31
|
+
- - ">="
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0
|
33
|
+
version: '0'
|
20
34
|
type: :development
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
|
-
- - "
|
38
|
+
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +72,28 @@ dependencies:
|
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.
|
75
|
+
version: 1.15.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.15.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-performance
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.11.0
|
62
90
|
type: :development
|
63
91
|
prerelease: false
|
64
92
|
version_requirements: !ruby/object:Gem::Requirement
|
65
93
|
requirements:
|
66
94
|
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.
|
96
|
+
version: 1.11.0
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
98
|
name: rubocop-rake
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,84 +114,84 @@ dependencies:
|
|
86
114
|
requirements:
|
87
115
|
- - "~>"
|
88
116
|
- !ruby/object:Gem::Version
|
89
|
-
version: 2.
|
117
|
+
version: 2.3.0
|
90
118
|
type: :development
|
91
119
|
prerelease: false
|
92
120
|
version_requirements: !ruby/object:Gem::Requirement
|
93
121
|
requirements:
|
94
122
|
- - "~>"
|
95
123
|
- !ruby/object:Gem::Version
|
96
|
-
version: 2.
|
124
|
+
version: 2.3.0
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
126
|
name: rubocop-sorbet
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
100
128
|
requirements:
|
101
129
|
- - "~>"
|
102
130
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.
|
131
|
+
version: 0.6.0
|
104
132
|
type: :development
|
105
133
|
prerelease: false
|
106
134
|
version_requirements: !ruby/object:Gem::Requirement
|
107
135
|
requirements:
|
108
136
|
- - "~>"
|
109
137
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
138
|
+
version: 0.6.0
|
111
139
|
- !ruby/object:Gem::Dependency
|
112
140
|
name: simplecov
|
113
141
|
requirement: !ruby/object:Gem::Requirement
|
114
142
|
requirements:
|
115
|
-
- - "
|
143
|
+
- - ">="
|
116
144
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0
|
145
|
+
version: '0'
|
118
146
|
type: :development
|
119
147
|
prerelease: false
|
120
148
|
version_requirements: !ruby/object:Gem::Requirement
|
121
149
|
requirements:
|
122
|
-
- - "
|
150
|
+
- - ">="
|
123
151
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0
|
152
|
+
version: '0'
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
154
|
name: sorbet
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
128
156
|
requirements:
|
129
157
|
- - "~>"
|
130
158
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.5.
|
159
|
+
version: 0.5.6289
|
132
160
|
type: :development
|
133
161
|
prerelease: false
|
134
162
|
version_requirements: !ruby/object:Gem::Requirement
|
135
163
|
requirements:
|
136
164
|
- - "~>"
|
137
165
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.5.
|
166
|
+
version: 0.5.6289
|
139
167
|
- !ruby/object:Gem::Dependency
|
140
168
|
name: sorbet-runtime
|
141
169
|
requirement: !ruby/object:Gem::Requirement
|
142
170
|
requirements:
|
143
171
|
- - ">="
|
144
172
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
173
|
+
version: 0.5.6289
|
146
174
|
type: :runtime
|
147
175
|
prerelease: false
|
148
176
|
version_requirements: !ruby/object:Gem::Requirement
|
149
177
|
requirements:
|
150
178
|
- - ">="
|
151
179
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
180
|
+
version: 0.5.6289
|
153
181
|
- !ruby/object:Gem::Dependency
|
154
182
|
name: yard
|
155
183
|
requirement: !ruby/object:Gem::Requirement
|
156
184
|
requirements:
|
157
185
|
- - ">="
|
158
186
|
- !ruby/object:Gem::Version
|
159
|
-
version:
|
187
|
+
version: 0.9.21
|
160
188
|
type: :runtime
|
161
189
|
prerelease: false
|
162
190
|
version_requirements: !ruby/object:Gem::Requirement
|
163
191
|
requirements:
|
164
192
|
- - ">="
|
165
193
|
- !ruby/object:Gem::Version
|
166
|
-
version:
|
194
|
+
version: 0.9.21
|
167
195
|
description: 'A YARD plugin that incorporates Sorbet type information
|
168
196
|
|
169
197
|
'
|
@@ -172,31 +200,27 @@ executables: []
|
|
172
200
|
extensions: []
|
173
201
|
extra_rdoc_files: []
|
174
202
|
files:
|
175
|
-
- ".editorconfig"
|
176
|
-
- ".gitignore"
|
177
|
-
- ".rspec"
|
178
|
-
- ".rubocop.yml"
|
179
|
-
- ".travis.yml"
|
180
|
-
- ".yardopts"
|
181
|
-
- CHANGELOG.md
|
182
|
-
- Gemfile
|
183
203
|
- LICENSE
|
184
|
-
- README.md
|
185
|
-
- Rakefile
|
186
204
|
- lib/yard-sorbet.rb
|
187
205
|
- lib/yard-sorbet/directives.rb
|
188
|
-
- lib/yard-sorbet/
|
206
|
+
- lib/yard-sorbet/handlers.rb
|
207
|
+
- lib/yard-sorbet/handlers/abstract_dsl_handler.rb
|
208
|
+
- lib/yard-sorbet/handlers/enums_handler.rb
|
209
|
+
- lib/yard-sorbet/handlers/sig_handler.rb
|
210
|
+
- lib/yard-sorbet/handlers/struct_handler.rb
|
211
|
+
- lib/yard-sorbet/node_utils.rb
|
189
212
|
- lib/yard-sorbet/sig_to_yard.rb
|
190
|
-
- lib/yard-sorbet/struct_handler.rb
|
191
213
|
- lib/yard-sorbet/version.rb
|
192
|
-
- yard-sorbet.gemspec
|
193
214
|
homepage: https://github.com/dduugg/yard-sorbet
|
194
215
|
licenses:
|
195
216
|
- Apache-2.0
|
196
217
|
metadata:
|
218
|
+
bug_tracker_uri: https://github.com/dduugg/yard-sorbet/issues
|
219
|
+
changelog_uri: https://github.com/dduugg/yard-sorbet/blob/master/CHANGELOG.md
|
220
|
+
documentation_uri: https://dduugg.github.io/yard-sorbet/
|
197
221
|
homepage_uri: https://github.com/dduugg/yard-sorbet
|
198
222
|
source_code_uri: https://github.com/dduugg/yard-sorbet
|
199
|
-
|
223
|
+
wiki_uri: https://github.com/dduugg/yard-sorbet/wiki
|
200
224
|
post_install_message:
|
201
225
|
rdoc_options: []
|
202
226
|
require_paths:
|
@@ -212,7 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
212
236
|
- !ruby/object:Gem::Version
|
213
237
|
version: '0'
|
214
238
|
requirements: []
|
215
|
-
rubygems_version: 3.
|
239
|
+
rubygems_version: 3.1.4
|
216
240
|
signing_key:
|
217
241
|
specification_version: 4
|
218
242
|
summary: Create YARD docs from Sorbet type signatures
|
data/.editorconfig
DELETED
data/.gitignore
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
*.gem
|
2
|
-
*.rbc
|
3
|
-
/.config
|
4
|
-
/coverage/
|
5
|
-
/InstalledFiles
|
6
|
-
/pkg/
|
7
|
-
/spec/reports/
|
8
|
-
/spec/examples.txt
|
9
|
-
/test/tmp/
|
10
|
-
/test/version_tmp/
|
11
|
-
/tmp/
|
12
|
-
|
13
|
-
# Used by dotenv library to load environment variables.
|
14
|
-
# .env
|
15
|
-
|
16
|
-
# Ignore Byebug command history file.
|
17
|
-
.byebug_history
|
18
|
-
|
19
|
-
## Documentation cache and generated files:
|
20
|
-
/.yardoc/
|
21
|
-
/_yardoc/
|
22
|
-
/doc/
|
23
|
-
/rdoc/
|
24
|
-
|
25
|
-
## Environment normalization:
|
26
|
-
/.bundle/
|
27
|
-
/vendor/bundle
|
28
|
-
/lib/bundler/man/
|
29
|
-
|
30
|
-
# for a library or gem, you might want to ignore these files since the code is
|
31
|
-
# intended to run in multiple environments; otherwise, check them in:
|
32
|
-
Gemfile.lock
|
33
|
-
.ruby-version
|
34
|
-
.ruby-gemset
|
35
|
-
|
36
|
-
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
37
|
-
.rvmrc
|
38
|
-
|
39
|
-
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
40
|
-
# .rubocop-https?--*
|
data/.rspec
DELETED
data/.rubocop.yml
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
---
|
2
|
-
require:
|
3
|
-
- rubocop-rake
|
4
|
-
- rubocop-rspec
|
5
|
-
- rubocop-sorbet
|
6
|
-
|
7
|
-
AllCops:
|
8
|
-
NewCops: enable
|
9
|
-
TargetRubyVersion: 2.5
|
10
|
-
|
11
|
-
Layout/ClassStructure:
|
12
|
-
Enabled: true
|
13
|
-
# doesn't recognize sorbet magic comments
|
14
|
-
Layout/EmptyLineAfterMagicComment:
|
15
|
-
Enabled: false
|
16
|
-
Layout/LineLength:
|
17
|
-
Enabled: false
|
18
|
-
Metrics/AbcSize:
|
19
|
-
Enabled: false
|
20
|
-
Metrics/BlockLength:
|
21
|
-
Enabled: false
|
22
|
-
Metrics/BlockNesting:
|
23
|
-
Enabled: false
|
24
|
-
Metrics/ClassLength:
|
25
|
-
Enabled: false
|
26
|
-
Metrics/CyclomaticComplexity:
|
27
|
-
Enabled: false
|
28
|
-
Metrics/MethodLength:
|
29
|
-
Enabled: false
|
30
|
-
Metrics/PerceivedComplexity:
|
31
|
-
Enabled: false
|
32
|
-
Naming/FileName:
|
33
|
-
Exclude:
|
34
|
-
- lib/yard-sorbet.rb
|
35
|
-
RSpec/EmptyExampleGroup:
|
36
|
-
Enabled: false
|
37
|
-
RSpec/ExampleLength:
|
38
|
-
Enabled: false
|
39
|
-
RSpec/MultipleExpectations:
|
40
|
-
Enabled: false
|
41
|
-
Sorbet/EnforceSignatures:
|
42
|
-
Enabled: false
|
43
|
-
Sorbet/StrictSigil:
|
44
|
-
Enabled: false
|
45
|
-
Sorbet/StrongSigil:
|
46
|
-
Enabled: false
|
47
|
-
Sorbet/TrueSigil:
|
48
|
-
Exclude:
|
49
|
-
- lib/yard-sorbet/struct_handler.rb
|
50
|
-
- spec/**/*
|
51
|
-
Style/AccessModifierDeclarations:
|
52
|
-
EnforcedStyle: inline
|
53
|
-
Style/ClassAndModuleChildren:
|
54
|
-
EnforcedStyle: compact
|
55
|
-
Style/NegatedIf:
|
56
|
-
Enabled: false
|
57
|
-
Style/NegatedWhile:
|
58
|
-
Enabled: false
|
59
|
-
Style/Next:
|
60
|
-
Enabled: false
|
61
|
-
Style/IfUnlessModifier:
|
62
|
-
Enabled: false
|
data/.travis.yml
DELETED
data/.yardopts
DELETED
data/CHANGELOG.md
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# Change log
|
2
|
-
|
3
|
-
## master (unreleased)
|
4
|
-
|
5
|
-
## 0.1.0 (2020-12-01)
|
6
|
-
|
7
|
-
### New features
|
8
|
-
|
9
|
-
* [#8](https://github.com/dduugg/yard-sorbet/pull/8): Add support for singleton class syntax.
|
10
|
-
* [#7](https://github.com/dduugg/yard-sorbet/pull/7): Add support for top-level constants in sigs.
|
11
|
-
|
12
|
-
### Bug fixes
|
13
|
-
|
14
|
-
* [#9](https://github.com/dduugg/yard-sorbet/pull/9): Remove warning for use of `T.attached_class`.
|
15
|
-
* [#11](https://github.com/dduugg/yard-sorbet/pull/11): Fix parsing of custom parameterized types.
|
16
|
-
* [#12](https://github.com/dduugg/yard-sorbet/pull/12): Fix parsing of recursive custom parameterized types.
|
17
|
-
|
18
|
-
### Changes
|
19
|
-
|
20
|
-
* [#10](https://github.com/dduugg/yard-sorbet/pull/10): Downgrade log level of unsupported `sig` `aref` nodes.
|
21
|
-
* Drop Ruby 2.4 support
|
22
|
-
|
23
|
-
## 0.0.1 (2020-01-24)
|
24
|
-
|
25
|
-
* [#1](https://github.com/dduugg/yard-sorbet/pull/1): Add `T::Struct` support.
|
26
|
-
* [#3](https://github.com/dduugg/yard-sorbet/pull/3): Rename require path to be conistent with gem name.
|
27
|
-
|
28
|
-
## 0.0.0 (2020-01-05)
|
29
|
-
|
30
|
-
* Initial Release
|
data/Gemfile
DELETED
data/README.md
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# yard-sorbet
|
2
|
-
[](https://badge.fury.io/rb/yard-sorbet)
|
3
|
-
[](https://travis-ci.com/dduugg/yard-sorbet)
|
4
|
-
[](https://codecov.io/gh/dduugg/yard-sorbet)
|
5
|
-
|
6
|
-
A YARD [plugin](https://rubydoc.info/gems/yard/file/docs/GettingStarted.md#Plugin_Support) that parses Sorbet type annotations
|
7
|
-
|
8
|
-
## Install
|
9
|
-
|
10
|
-
```shell
|
11
|
-
gem install yard-sorbet
|
12
|
-
```
|
13
|
-
|
14
|
-
## Usage
|
15
|
-
```bash
|
16
|
-
yard doc --plugin sorbet
|
17
|
-
```
|
data/Rakefile
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# typed: strong
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require 'bundler/gem_tasks'
|
5
|
-
require 'rspec/core/rake_task'
|
6
|
-
|
7
|
-
RSpec::Core::RakeTask.new(:spec)
|
8
|
-
|
9
|
-
desc 'Lint files with Rubocop'
|
10
|
-
task :lint do
|
11
|
-
sh 'rubocop'
|
12
|
-
end
|
13
|
-
|
14
|
-
desc 'Typecheck files with Sorbet'
|
15
|
-
task :typecheck do
|
16
|
-
sh 'srb tc --ignore=vendor/'
|
17
|
-
end
|
18
|
-
|
19
|
-
task default: %i[lint spec typecheck]
|
@@ -1,147 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# A YARD Handler for Sorbet type declarations
|
5
|
-
class YARDSorbet::SigHandler < YARD::Handlers::Ruby::Base
|
6
|
-
extend T::Sig
|
7
|
-
handles :class, :module, :singleton_class?
|
8
|
-
|
9
|
-
sig { returns(String).checked(:never) }
|
10
|
-
def process
|
11
|
-
# Find the list of declarations inside the class
|
12
|
-
class_def = statement.children.find { |c| c.type == :list }
|
13
|
-
class_contents = class_def.children
|
14
|
-
|
15
|
-
process_class_contents(class_contents)
|
16
|
-
end
|
17
|
-
|
18
|
-
private def process_class_contents(class_contents)
|
19
|
-
class_contents.each_with_index do |child, i|
|
20
|
-
if child.type == :sclass && child.children.size == 2 && child.children[1].type == :list
|
21
|
-
singleton_class_contents = child.children[1]
|
22
|
-
process_class_contents(singleton_class_contents)
|
23
|
-
end
|
24
|
-
next unless type_signature?(child)
|
25
|
-
|
26
|
-
next_statement = class_contents[i + 1]
|
27
|
-
next unless processable_method?(next_statement)
|
28
|
-
|
29
|
-
process_method_definition(next_statement, child)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
private def processable_method?(next_statement)
|
34
|
-
%i[def defs command].include?(next_statement&.type) && !next_statement.docstring
|
35
|
-
end
|
36
|
-
|
37
|
-
private def process_method_definition(method_node, sig_node)
|
38
|
-
# Swap the method definition docstring and the sig docstring.
|
39
|
-
# Parse relevant parts of the `sig` and include them as well.
|
40
|
-
docstring, directives = YARDSorbet::Directives.extract_directives(sig_node.docstring)
|
41
|
-
parsed_sig = parse_sig(sig_node)
|
42
|
-
enhance_tag(docstring, :abstract, parsed_sig)
|
43
|
-
enhance_tag(docstring, :return, parsed_sig)
|
44
|
-
if method_node.type != :command
|
45
|
-
parsed_sig[:params]&.each do |name, types|
|
46
|
-
enhance_param(docstring, name, types)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
method_node.docstring = docstring.to_raw
|
50
|
-
YARDSorbet::Directives.add_directives(method_node.docstring, directives)
|
51
|
-
sig_node.docstring = nil
|
52
|
-
end
|
53
|
-
|
54
|
-
private def enhance_param(docstring, name, types)
|
55
|
-
tag = docstring.tags.find { |t| t.tag_name == 'param' && t.name == name }
|
56
|
-
if tag
|
57
|
-
docstring.delete_tag_if { |t| t == tag }
|
58
|
-
tag.types = types
|
59
|
-
else
|
60
|
-
tag = YARD::Tags::Tag.new(:param, '', types, name)
|
61
|
-
end
|
62
|
-
docstring.add_tag(tag)
|
63
|
-
end
|
64
|
-
|
65
|
-
private def enhance_tag(docstring, type, parsed_sig)
|
66
|
-
return if !parsed_sig[type]
|
67
|
-
|
68
|
-
tag = docstring.tags.find { |t| t.tag_name == type.to_s }
|
69
|
-
if tag
|
70
|
-
docstring.delete_tags(type)
|
71
|
-
else
|
72
|
-
tag = YARD::Tags::Tag.new(type, '')
|
73
|
-
end
|
74
|
-
if parsed_sig[type].is_a?(Array)
|
75
|
-
tag.types = parsed_sig[type]
|
76
|
-
end
|
77
|
-
docstring.add_tag(tag)
|
78
|
-
end
|
79
|
-
|
80
|
-
private def parse_sig(sig_node)
|
81
|
-
parsed = {}
|
82
|
-
parsed[:abstract] = false
|
83
|
-
parsed[:params] = {}
|
84
|
-
found_params = T.let(false, T::Boolean)
|
85
|
-
found_return = T.let(false, T::Boolean)
|
86
|
-
bfs_traverse(sig_node, exclude: %i[array hash]) do |n|
|
87
|
-
if n.source == 'abstract'
|
88
|
-
parsed[:abstract] = true
|
89
|
-
elsif n.source == 'params' && !found_params
|
90
|
-
found_params = true
|
91
|
-
sibling = T.must(sibling_node(n))
|
92
|
-
bfs_traverse(sibling, exclude: %i[array call hash]) do |p|
|
93
|
-
if p.type == :assoc
|
94
|
-
param_name = p.children.first.source[0...-1]
|
95
|
-
types = YARDSorbet::SigToYARD.convert(p.children.last)
|
96
|
-
parsed[:params][param_name] = types
|
97
|
-
end
|
98
|
-
end
|
99
|
-
elsif n.source == 'returns' && !found_return
|
100
|
-
found_return = true
|
101
|
-
parsed[:return] = YARDSorbet::SigToYARD.convert(T.must(sibling_node(n)))
|
102
|
-
elsif n.source == 'void'
|
103
|
-
parsed[:return] ||= ['void']
|
104
|
-
end
|
105
|
-
end
|
106
|
-
parsed
|
107
|
-
end
|
108
|
-
|
109
|
-
# Returns true if the given node is part of a type signature.
|
110
|
-
private def type_signature?(node)
|
111
|
-
loop do
|
112
|
-
return false if node.nil?
|
113
|
-
return false unless %i[call vcall fcall].include?(node.type)
|
114
|
-
return true if T.unsafe(node).method_name(true) == :sig
|
115
|
-
|
116
|
-
node = node.children.first
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
private def sibling_node(node)
|
121
|
-
found_sibling = T.let(false, T::Boolean)
|
122
|
-
node.parent.children.each do |n|
|
123
|
-
if found_sibling
|
124
|
-
return n
|
125
|
-
end
|
126
|
-
|
127
|
-
if n == node
|
128
|
-
found_sibling = true
|
129
|
-
end
|
130
|
-
end
|
131
|
-
nil
|
132
|
-
end
|
133
|
-
|
134
|
-
# @yield [YARD::Parser::Ruby::AstNode]
|
135
|
-
private def bfs_traverse(node, exclude: [])
|
136
|
-
queue = [node]
|
137
|
-
while !queue.empty?
|
138
|
-
n = T.must(queue.shift)
|
139
|
-
yield n
|
140
|
-
n.children.each do |c|
|
141
|
-
if !exclude.include?(c.type)
|
142
|
-
queue.push(c)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
data/yard-sorbet.gemspec
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# typed: strong
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require_relative 'lib/yard-sorbet/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = 'yard-sorbet'
|
8
|
-
spec.version = YARDSorbet::VERSION
|
9
|
-
spec.author = 'Douglas Eichelberger'
|
10
|
-
spec.email = 'dduugg@gmail.com'
|
11
|
-
spec.license = 'Apache-2.0'
|
12
|
-
|
13
|
-
spec.summary = 'Create YARD docs from Sorbet type signatures'
|
14
|
-
spec.description = <<~DESC
|
15
|
-
A YARD plugin that incorporates Sorbet type information
|
16
|
-
DESC
|
17
|
-
spec.homepage = 'https://github.com/dduugg/yard-sorbet'
|
18
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
19
|
-
|
20
|
-
spec.metadata['homepage_uri'] = spec.homepage
|
21
|
-
spec.metadata['source_code_uri'] = spec.homepage
|
22
|
-
spec.metadata['changelog_uri'] = 'https://github.com/dduugg/yard-sorbet/blob/master/CHANGELOG.md'
|
23
|
-
|
24
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(sorbet|spec)/}) }
|
26
|
-
end
|
27
|
-
|
28
|
-
spec.add_development_dependency 'codecov', '~> 0.2.12'
|
29
|
-
spec.add_development_dependency 'rake', '~> 13.0'
|
30
|
-
spec.add_development_dependency 'rspec', '~> 3.10'
|
31
|
-
spec.add_development_dependency 'rubocop', '~> 1.5.0'
|
32
|
-
spec.add_development_dependency 'rubocop-rake', '~> 0.5.1'
|
33
|
-
spec.add_development_dependency 'rubocop-rspec', '~> 2.0.0'
|
34
|
-
spec.add_development_dependency 'rubocop-sorbet', '~> 0.5.1'
|
35
|
-
spec.add_development_dependency 'simplecov', '~> 0.20.0'
|
36
|
-
spec.add_development_dependency 'sorbet', '~> 0.5.6083'
|
37
|
-
spec.add_runtime_dependency 'sorbet-runtime', '>= 0.5'
|
38
|
-
spec.add_runtime_dependency 'yard', '>= 0.9'
|
39
|
-
end
|