docscribe 1.0.0 → 1.2.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/README.md +692 -180
- data/exe/docscribe +2 -74
- data/lib/docscribe/cli/config_builder.rb +62 -0
- data/lib/docscribe/cli/init.rb +58 -0
- data/lib/docscribe/cli/options.rb +204 -0
- data/lib/docscribe/cli/run.rb +415 -0
- data/lib/docscribe/cli.rb +31 -0
- data/lib/docscribe/config/defaults.rb +71 -0
- data/lib/docscribe/config/emit.rb +126 -0
- data/lib/docscribe/config/filtering.rb +160 -0
- data/lib/docscribe/config/loader.rb +59 -0
- data/lib/docscribe/config/rbs.rb +51 -0
- data/lib/docscribe/config/sorbet.rb +87 -0
- data/lib/docscribe/config/sorting.rb +23 -0
- data/lib/docscribe/config/template.rb +176 -0
- data/lib/docscribe/config/utils.rb +102 -0
- data/lib/docscribe/config.rb +20 -230
- data/lib/docscribe/infer/ast_walk.rb +28 -0
- data/lib/docscribe/infer/constants.rb +11 -0
- data/lib/docscribe/infer/literals.rb +55 -0
- data/lib/docscribe/infer/names.rb +43 -0
- data/lib/docscribe/infer/params.rb +62 -0
- data/lib/docscribe/infer/raises.rb +68 -0
- data/lib/docscribe/infer/returns.rb +171 -0
- data/lib/docscribe/infer.rb +110 -259
- data/lib/docscribe/inline_rewriter/collector.rb +845 -0
- data/lib/docscribe/inline_rewriter/doc_block.rb +383 -0
- data/lib/docscribe/inline_rewriter/doc_builder.rb +605 -0
- data/lib/docscribe/inline_rewriter/source_helpers.rb +228 -0
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +244 -0
- data/lib/docscribe/inline_rewriter.rb +604 -425
- data/lib/docscribe/parsing.rb +120 -0
- data/lib/docscribe/types/provider_chain.rb +37 -0
- data/lib/docscribe/types/rbs/provider.rb +213 -0
- data/lib/docscribe/types/rbs/type_formatter.rb +132 -0
- data/lib/docscribe/types/signature.rb +65 -0
- data/lib/docscribe/types/sorbet/base_provider.rb +217 -0
- data/lib/docscribe/types/sorbet/rbi_provider.rb +35 -0
- data/lib/docscribe/types/sorbet/source_provider.rb +25 -0
- data/lib/docscribe/version.rb +1 -1
- data/lib/docscribe.rb +1 -0
- metadata +85 -17
- data/.rspec +0 -3
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -73
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -6
- data/Gemfile.lock +0 -73
- data/Rakefile +0 -12
- data/rakelib/docs.rake +0 -73
- data/stingray_docs_internal.gemspec +0 -41
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'parser/source/buffer'
|
|
4
|
+
require 'rubygems' # for Gem::Version
|
|
5
|
+
|
|
6
|
+
module Docscribe
|
|
7
|
+
# Parser backend selection for Docscribe.
|
|
8
|
+
#
|
|
9
|
+
# Docscribe always works with parser-gem-compatible AST nodes (`Parser::AST::Node`)
|
|
10
|
+
# and parser source locations (`Parser::Source::*`) because rewriting relies on
|
|
11
|
+
# `Parser::Source::TreeRewriter`.
|
|
12
|
+
#
|
|
13
|
+
# On Ruby 3.4+, Prism can parse newer syntax before the parser gem fully supports it,
|
|
14
|
+
# so Docscribe can use Prism and translate the result into parser-gem-compatible nodes.
|
|
15
|
+
#
|
|
16
|
+
# Backends:
|
|
17
|
+
# - `:parser` => parser gem
|
|
18
|
+
# - `:prism` => Prism + translation
|
|
19
|
+
# - `:auto` => choose based on runtime Ruby version or env override
|
|
20
|
+
#
|
|
21
|
+
# You can force a backend with:
|
|
22
|
+
# - `DOCSCRIBE_PARSER_BACKEND=parser`
|
|
23
|
+
# - `DOCSCRIBE_PARSER_BACKEND=prism`
|
|
24
|
+
module Parsing
|
|
25
|
+
class << self
|
|
26
|
+
# Parse source code into a parser-gem-compatible AST.
|
|
27
|
+
#
|
|
28
|
+
# @param [String] code Ruby source
|
|
29
|
+
# @param [String] file source name used for parser locations
|
|
30
|
+
# @param [Symbol] backend :auto, :parser, or :prism
|
|
31
|
+
# @return [Parser::AST::Node, nil]
|
|
32
|
+
def parse(code, file: '(docscribe)', backend: :auto)
|
|
33
|
+
buffer = Parser::Source::Buffer.new(file, source: code)
|
|
34
|
+
parse_buffer(buffer, backend: backend)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Parse a prepared source buffer into a parser-gem-compatible AST.
|
|
38
|
+
#
|
|
39
|
+
# @param [Parser::Source::Buffer] buffer
|
|
40
|
+
# @param [Symbol] backend :auto, :parser, or :prism
|
|
41
|
+
# @return [Parser::AST::Node, nil]
|
|
42
|
+
def parse_buffer(buffer, backend: :auto)
|
|
43
|
+
parser = parser_for(backend: backend)
|
|
44
|
+
parser.parse(buffer)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Parse source code and also return comments when supported by the backend.
|
|
48
|
+
#
|
|
49
|
+
# @param [String] code Ruby source
|
|
50
|
+
# @param [String] file source name used for parser locations
|
|
51
|
+
# @param [Symbol] backend :auto, :parser, or :prism
|
|
52
|
+
# @return [Array<(Parser::AST::Node, Array)>]
|
|
53
|
+
def parse_with_comments(code, file: '(docscribe)', backend: :auto)
|
|
54
|
+
buffer = Parser::Source::Buffer.new(file, source: code)
|
|
55
|
+
parse_with_comments_buffer(buffer, backend: backend)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Parse a prepared source buffer and also return comments when supported by the backend.
|
|
59
|
+
#
|
|
60
|
+
# @param [Parser::Source::Buffer] buffer
|
|
61
|
+
# @param [Symbol] backend :auto, :parser, or :prism
|
|
62
|
+
# @return [Array<(Parser::AST::Node, Array)>]
|
|
63
|
+
def parse_with_comments_buffer(buffer, backend: :auto)
|
|
64
|
+
parser = parser_for(backend: backend)
|
|
65
|
+
parser.parse_with_comments(buffer)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Build the backend-specific parser object.
|
|
71
|
+
#
|
|
72
|
+
# @private
|
|
73
|
+
# @param [Symbol] backend
|
|
74
|
+
# @return [Object]
|
|
75
|
+
def parser_for(backend: :auto)
|
|
76
|
+
case backend(backend)
|
|
77
|
+
when :parser
|
|
78
|
+
require 'parser/current'
|
|
79
|
+
Parser::CurrentRuby.new
|
|
80
|
+
when :prism
|
|
81
|
+
require 'prism'
|
|
82
|
+
Prism::Translation::ParserCurrent.new
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Resolve the effective parser backend.
|
|
87
|
+
#
|
|
88
|
+
# Resolution order:
|
|
89
|
+
# - `DOCSCRIBE_PARSER_BACKEND` env var, if set
|
|
90
|
+
# - explicit `backend:` argument
|
|
91
|
+
# - auto choice based on Ruby version
|
|
92
|
+
#
|
|
93
|
+
# @private
|
|
94
|
+
# @param [Symbol] backend requested backend
|
|
95
|
+
# @raise [ArgumentError]
|
|
96
|
+
# @return [Symbol] :parser or :prism
|
|
97
|
+
def backend(backend = :auto)
|
|
98
|
+
env = ENV.fetch('DOCSCRIBE_PARSER_BACKEND') { nil }
|
|
99
|
+
backend = env.to_sym if env && !env.empty?
|
|
100
|
+
|
|
101
|
+
case backend
|
|
102
|
+
when :auto
|
|
103
|
+
ruby_gte_34? ? :prism : :parser
|
|
104
|
+
when :parser, :prism
|
|
105
|
+
backend
|
|
106
|
+
else
|
|
107
|
+
raise ArgumentError, "Unknown backend: #{backend.inspect}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Whether the current Ruby version is 3.4 or newer.
|
|
112
|
+
#
|
|
113
|
+
# @private
|
|
114
|
+
# @return [Boolean]
|
|
115
|
+
def ruby_gte_34?
|
|
116
|
+
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4')
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docscribe
|
|
4
|
+
module Types
|
|
5
|
+
# Resolve method signatures by querying a list of providers in order.
|
|
6
|
+
#
|
|
7
|
+
# The first provider that returns a non-nil signature wins.
|
|
8
|
+
#
|
|
9
|
+
# This lets Docscribe combine multiple external type sources behind one
|
|
10
|
+
# interface, for example:
|
|
11
|
+
# - inline Sorbet signatures in the current file
|
|
12
|
+
# - Sorbet RBI files
|
|
13
|
+
# - RBS files
|
|
14
|
+
class ProviderChain
|
|
15
|
+
# @param [Array<#signature_for>] providers ordered signature providers
|
|
16
|
+
# @return [Object]
|
|
17
|
+
def initialize(*providers)
|
|
18
|
+
@providers = providers.compact
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Resolve a method signature from the first provider that can supply it.
|
|
22
|
+
#
|
|
23
|
+
# @param [String] container e.g. "MyModule::MyClass"
|
|
24
|
+
# @param [Symbol] scope :instance or :class
|
|
25
|
+
# @param [Symbol, String] name method name
|
|
26
|
+
# @return [Docscribe::Types::MethodSignature, nil]
|
|
27
|
+
def signature_for(container:, scope:, name:)
|
|
28
|
+
@providers.each do |provider|
|
|
29
|
+
sig = provider.signature_for(container: container, scope: scope, name: name)
|
|
30
|
+
return sig if sig
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pathname'
|
|
4
|
+
require 'docscribe/types/signature'
|
|
5
|
+
require 'docscribe/types/rbs/type_formatter'
|
|
6
|
+
|
|
7
|
+
module Docscribe
|
|
8
|
+
module Types
|
|
9
|
+
module RBS
|
|
10
|
+
# Resolve method signatures from `.rbs` files using the official RBS
|
|
11
|
+
# environment and definition builder APIs.
|
|
12
|
+
#
|
|
13
|
+
# The provider returns Docscribe's normalized signature model so the rest of
|
|
14
|
+
# the pipeline can stay independent of the underlying signature source.
|
|
15
|
+
class Provider
|
|
16
|
+
# @param [Array<String>] sig_dirs directories containing `.rbs` files
|
|
17
|
+
# @param [Boolean] collapse_generics whether generic container types
|
|
18
|
+
# should be simplified during formatting
|
|
19
|
+
# @return [Object]
|
|
20
|
+
def initialize(sig_dirs:, collapse_generics: false)
|
|
21
|
+
require 'rbs'
|
|
22
|
+
@sig_dirs = Array(sig_dirs).map(&:to_s)
|
|
23
|
+
@collapse_generics = !!collapse_generics
|
|
24
|
+
@env = nil
|
|
25
|
+
@builder = nil
|
|
26
|
+
@warned = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Look up a normalized method signature from loaded RBS definitions.
|
|
30
|
+
#
|
|
31
|
+
# Returns nil when the method cannot be resolved or when RBS lookup fails.
|
|
32
|
+
#
|
|
33
|
+
# @param [String] container e.g. "MyModule::MyClass"
|
|
34
|
+
# @param [Symbol] scope :instance or :class
|
|
35
|
+
# @param [Symbol, String] name method name
|
|
36
|
+
# @raise [::RBS::BaseError]
|
|
37
|
+
# @raise [StandardError]
|
|
38
|
+
# @return [Docscribe::Types::MethodSignature, nil]
|
|
39
|
+
def signature_for(container:, scope:, name:)
|
|
40
|
+
load_env!
|
|
41
|
+
|
|
42
|
+
definition = definition_for(container: container, scope: scope)
|
|
43
|
+
method_def = definition.methods[name.to_sym]
|
|
44
|
+
return nil unless method_def
|
|
45
|
+
|
|
46
|
+
method_type = method_def.method_types.first
|
|
47
|
+
return nil unless method_type
|
|
48
|
+
|
|
49
|
+
func = method_type.type
|
|
50
|
+
build_signature(func)
|
|
51
|
+
rescue ::RBS::BaseError => e
|
|
52
|
+
warn_once("Docscribe: RBS error: #{e.class}: #{e.message}")
|
|
53
|
+
nil
|
|
54
|
+
rescue StandardError => e
|
|
55
|
+
warn_once(
|
|
56
|
+
'Docscribe: RBS integration failed (falling back to inference): ' \
|
|
57
|
+
"#{e.class}: #{e.message}\nFeel free to open an issue on github."
|
|
58
|
+
)
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# Lazily load and resolve the RBS environment.
|
|
65
|
+
#
|
|
66
|
+
# @private
|
|
67
|
+
# @return [void]
|
|
68
|
+
def load_env!
|
|
69
|
+
return if @env && @builder
|
|
70
|
+
|
|
71
|
+
loader = ::RBS::EnvironmentLoader.new
|
|
72
|
+
|
|
73
|
+
@sig_dirs.each do |dir|
|
|
74
|
+
path = Pathname(dir)
|
|
75
|
+
loader.add(path: path) if path.directory?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@env = ::RBS::Environment.from_loader(loader).resolve_type_names
|
|
79
|
+
@builder = ::RBS::DefinitionBuilder.new(env: @env)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Build the appropriate instance or singleton definition for a container.
|
|
83
|
+
#
|
|
84
|
+
# @private
|
|
85
|
+
# @param [String] container
|
|
86
|
+
# @param [Symbol] scope
|
|
87
|
+
# @return [Object]
|
|
88
|
+
def definition_for(container:, scope:)
|
|
89
|
+
type_name = ::RBS::TypeName.parse(absolute_const(container))
|
|
90
|
+
scope == :class ? @builder.build_singleton(type_name) : @builder.build_instance(type_name)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Normalize a container name into an absolute constant path.
|
|
94
|
+
#
|
|
95
|
+
# @private
|
|
96
|
+
# @param [String] container
|
|
97
|
+
# @return [String]
|
|
98
|
+
def absolute_const(container)
|
|
99
|
+
s = container.to_s
|
|
100
|
+
s.start_with?('::') ? s : "::#{s}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Convert an RBS function type into Docscribe's simplified signature
|
|
104
|
+
# model.
|
|
105
|
+
#
|
|
106
|
+
# @private
|
|
107
|
+
# @param [::RBS::Types::Function] func
|
|
108
|
+
# @return [Docscribe::Types::MethodSignature]
|
|
109
|
+
def build_signature(func)
|
|
110
|
+
MethodSignature.new(
|
|
111
|
+
return_type: format_type(func.return_type),
|
|
112
|
+
param_types: build_param_types(func),
|
|
113
|
+
rest_positional: build_rest_positional(func),
|
|
114
|
+
rest_keywords: build_rest_keywords(func)
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Build a name => type map for positional and keyword parameters.
|
|
119
|
+
#
|
|
120
|
+
# @private
|
|
121
|
+
# @param [::RBS::Types::Function] func
|
|
122
|
+
# @return [Hash{String => String}]
|
|
123
|
+
def build_param_types(func)
|
|
124
|
+
param_types = {}
|
|
125
|
+
|
|
126
|
+
add_positionals!(param_types, func.required_positionals)
|
|
127
|
+
add_positionals!(param_types, func.optional_positionals)
|
|
128
|
+
add_positionals!(param_types, func.trailing_positionals)
|
|
129
|
+
|
|
130
|
+
func.required_keywords.each do |kw, p|
|
|
131
|
+
param_types[kw.to_s] = format_type(p.type)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
func.optional_keywords.each do |kw, p|
|
|
135
|
+
param_types[kw.to_s] = format_type(p.type)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
param_types
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Add named positional parameters to the normalized parameter map.
|
|
142
|
+
#
|
|
143
|
+
# @private
|
|
144
|
+
# @param [Hash{String => String}] param_types
|
|
145
|
+
# @param [Array<Object>] list
|
|
146
|
+
# @return [void]
|
|
147
|
+
def add_positionals!(param_types, list)
|
|
148
|
+
list.each do |p|
|
|
149
|
+
next unless p.name
|
|
150
|
+
|
|
151
|
+
param_types[p.name.to_s] = format_type(p.type)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Build normalized `*args` metadata.
|
|
156
|
+
#
|
|
157
|
+
# @private
|
|
158
|
+
# @param [::RBS::Types::Function] func
|
|
159
|
+
# @return [Docscribe::Types::RestPositional, nil]
|
|
160
|
+
def build_rest_positional(func)
|
|
161
|
+
rp = func.rest_positionals
|
|
162
|
+
return nil unless rp
|
|
163
|
+
|
|
164
|
+
RestPositional.new(
|
|
165
|
+
name: rp.name&.to_s,
|
|
166
|
+
element_type: format_type(rp.type)
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Build normalized `**kwargs` metadata.
|
|
171
|
+
#
|
|
172
|
+
# @private
|
|
173
|
+
# @param [::RBS::Types::Function] func
|
|
174
|
+
# @return [Docscribe::Types::RestKeywords, nil]
|
|
175
|
+
def build_rest_keywords(func)
|
|
176
|
+
rk = func.rest_keywords
|
|
177
|
+
return nil unless rk
|
|
178
|
+
|
|
179
|
+
RestKeywords.new(
|
|
180
|
+
name: rk.name&.to_s,
|
|
181
|
+
type: format_type(rk.type)
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Format an RBS type object into the YARD-ish type syntax used by
|
|
186
|
+
# generated comments.
|
|
187
|
+
#
|
|
188
|
+
# @private
|
|
189
|
+
# @param [Object] type
|
|
190
|
+
# @return [String]
|
|
191
|
+
def format_type(type)
|
|
192
|
+
Docscribe::Types::RBS::TypeFormatter.to_yard(
|
|
193
|
+
type,
|
|
194
|
+
collapse_generics: @collapse_generics
|
|
195
|
+
)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Print one debug warning per provider instance when debugging is enabled.
|
|
199
|
+
#
|
|
200
|
+
# @private
|
|
201
|
+
# @param [String] msg
|
|
202
|
+
# @return [void]
|
|
203
|
+
def warn_once(msg)
|
|
204
|
+
return unless ENV['DOCSCRIBE_RBS_DEBUG'] == '1'
|
|
205
|
+
return if @warned
|
|
206
|
+
|
|
207
|
+
@warned = true
|
|
208
|
+
warn msg
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docscribe
|
|
4
|
+
module Types
|
|
5
|
+
module RBS
|
|
6
|
+
# Convert RBS type objects into YARD-ish type strings.
|
|
7
|
+
#
|
|
8
|
+
# This is intentionally best-effort formatting: YARD type syntax is simpler than RBS,
|
|
9
|
+
# so some information is collapsed or approximated.
|
|
10
|
+
module TypeFormatter
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Convert one RBS type object into a YARD-ish string.
|
|
14
|
+
#
|
|
15
|
+
# Supported categories include:
|
|
16
|
+
# - base types (`bool`, `nil`, `void`, `untyped`)
|
|
17
|
+
# - optional and union types
|
|
18
|
+
# - named types with optional generic arguments
|
|
19
|
+
# - literal types
|
|
20
|
+
# - Proc types
|
|
21
|
+
#
|
|
22
|
+
# @note module_function: when included, also defines #to_yard (instance visibility: private)
|
|
23
|
+
# @param [Object] type RBS type object
|
|
24
|
+
# @param [Boolean] collapse_generics whether generic arguments should be omitted
|
|
25
|
+
# @return [String]
|
|
26
|
+
def to_yard(type, collapse_generics: false)
|
|
27
|
+
return 'Object' unless type
|
|
28
|
+
|
|
29
|
+
# RBS is loaded lazily by the provider; constants below exist only when rbs is available.
|
|
30
|
+
case type
|
|
31
|
+
when ::RBS::Types::Bases::Any
|
|
32
|
+
'Object'
|
|
33
|
+
when ::RBS::Types::Bases::Bool
|
|
34
|
+
'Boolean'
|
|
35
|
+
when ::RBS::Types::Bases::Void
|
|
36
|
+
'void'
|
|
37
|
+
when ::RBS::Types::Bases::Nil
|
|
38
|
+
'nil'
|
|
39
|
+
when ::RBS::Types::Optional
|
|
40
|
+
"#{to_yard(type.type, collapse_generics: collapse_generics)}?"
|
|
41
|
+
when ::RBS::Types::Union
|
|
42
|
+
format_union(type, collapse_generics: collapse_generics)
|
|
43
|
+
when ::RBS::Types::ClassInstance,
|
|
44
|
+
::RBS::Types::ClassSingleton,
|
|
45
|
+
::RBS::Types::Interface,
|
|
46
|
+
::RBS::Types::Alias
|
|
47
|
+
format_named(type, collapse_generics: collapse_generics)
|
|
48
|
+
when ::RBS::Types::Literal
|
|
49
|
+
literal_to_yard(type.literal)
|
|
50
|
+
when ::RBS::Types::Proc
|
|
51
|
+
'Proc'
|
|
52
|
+
else
|
|
53
|
+
fallback_string(type)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Format an RBS union type as a comma-separated YARD union.
|
|
58
|
+
#
|
|
59
|
+
# Example:
|
|
60
|
+
# - `String | Integer | nil` => `"String, Integer, nil"`
|
|
61
|
+
#
|
|
62
|
+
# @note module_function: when included, also defines #format_union (instance visibility: private)
|
|
63
|
+
# @param [::RBS::Types::Union] type
|
|
64
|
+
# @param [Boolean] collapse_generics
|
|
65
|
+
# @return [String]
|
|
66
|
+
def format_union(type, collapse_generics:)
|
|
67
|
+
type.types.map { |t| to_yard(t, collapse_generics: collapse_generics) }
|
|
68
|
+
.uniq
|
|
69
|
+
.join(', ')
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Format a named RBS type, optionally preserving generic arguments.
|
|
73
|
+
#
|
|
74
|
+
# Examples:
|
|
75
|
+
# - `::String` => `"String"`
|
|
76
|
+
# - `::Hash[::Symbol, untyped]` => `"Hash<Symbol, Object>"`
|
|
77
|
+
# - with `collapse_generics: true` => `"Hash"`
|
|
78
|
+
#
|
|
79
|
+
# @note module_function: when included, also defines #format_named (instance visibility: private)
|
|
80
|
+
# @param [Object] type named RBS type
|
|
81
|
+
# @param [Boolean] collapse_generics
|
|
82
|
+
# @return [String]
|
|
83
|
+
def format_named(type, collapse_generics:)
|
|
84
|
+
name = type.name.to_s.delete_prefix('::')
|
|
85
|
+
args = type.respond_to?(:args) ? type.args : []
|
|
86
|
+
|
|
87
|
+
if args && !args.empty?
|
|
88
|
+
return name if collapse_generics
|
|
89
|
+
|
|
90
|
+
"#{name}<#{args.map { |a| to_yard(a, collapse_generics: collapse_generics) }.join(', ')}>"
|
|
91
|
+
else
|
|
92
|
+
name
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Map a literal Ruby value from an RBS literal type into a YARD-ish type name.
|
|
97
|
+
#
|
|
98
|
+
# @note module_function: when included, also defines #literal_to_yard (instance visibility: private)
|
|
99
|
+
# @param [Object] lit literal value
|
|
100
|
+
# @return [String]
|
|
101
|
+
def literal_to_yard(lit)
|
|
102
|
+
case lit
|
|
103
|
+
when Integer then 'Integer'
|
|
104
|
+
when Float then 'Float'
|
|
105
|
+
when String then 'String'
|
|
106
|
+
when Symbol then 'Symbol'
|
|
107
|
+
when TrueClass, FalseClass then 'Boolean'
|
|
108
|
+
when NilClass then 'nil'
|
|
109
|
+
else 'Object'
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Fallback string conversion for unsupported or unexpected RBS type objects.
|
|
114
|
+
#
|
|
115
|
+
# Performs a few normalizations for nicer YARD output:
|
|
116
|
+
# - strips leading `::`
|
|
117
|
+
# - converts `bool` to `Boolean`
|
|
118
|
+
# - converts `untyped` to `Object`
|
|
119
|
+
#
|
|
120
|
+
# @note module_function: when included, also defines #fallback_string (instance visibility: private)
|
|
121
|
+
# @param [Object] type
|
|
122
|
+
# @return [String]
|
|
123
|
+
def fallback_string(type)
|
|
124
|
+
type.to_s
|
|
125
|
+
.gsub(/\A::/, '')
|
|
126
|
+
.gsub(/\bbool\b/, 'Boolean')
|
|
127
|
+
.gsub(/\buntyped\b/, 'Object')
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docscribe
|
|
4
|
+
module Types
|
|
5
|
+
# Simplified view of an RBS method signature for Docscribe.
|
|
6
|
+
#
|
|
7
|
+
# @!attribute return_type
|
|
8
|
+
# @return [String] formatted return type for YARD output
|
|
9
|
+
# @!attribute param_types
|
|
10
|
+
# @return [Hash{String=>String}] mapping of parameter name to formatted type
|
|
11
|
+
# @!attribute rest_positional
|
|
12
|
+
# @return [RestPositional, nil] info for `*args`
|
|
13
|
+
# @!attribute rest_keywords
|
|
14
|
+
# @return [RestKeywords, nil] info for `**kwargs`
|
|
15
|
+
#
|
|
16
|
+
# @!attribute [rw] return_type
|
|
17
|
+
# @return [Object]
|
|
18
|
+
# @param [Object] value
|
|
19
|
+
#
|
|
20
|
+
# @!attribute [rw] param_types
|
|
21
|
+
# @return [Object]
|
|
22
|
+
# @param [Object] value
|
|
23
|
+
#
|
|
24
|
+
# @!attribute [rw] rest_positional
|
|
25
|
+
# @return [Object]
|
|
26
|
+
# @param [Object] value
|
|
27
|
+
#
|
|
28
|
+
# @!attribute [rw] rest_keywords
|
|
29
|
+
# @return [Object]
|
|
30
|
+
# @param [Object] value
|
|
31
|
+
MethodSignature = Struct.new(:return_type, :param_types, :rest_positional, :rest_keywords, keyword_init: true)
|
|
32
|
+
|
|
33
|
+
# Simplified representation of an RBS rest-positional parameter.
|
|
34
|
+
#
|
|
35
|
+
# @!attribute name
|
|
36
|
+
# @return [String, nil] parameter name in RBS, if present
|
|
37
|
+
# @!attribute element_type
|
|
38
|
+
# @return [String] formatted element type
|
|
39
|
+
#
|
|
40
|
+
# @!attribute [rw] name
|
|
41
|
+
# @return [Object]
|
|
42
|
+
# @param [Object] value
|
|
43
|
+
#
|
|
44
|
+
# @!attribute [rw] element_type
|
|
45
|
+
# @return [Object]
|
|
46
|
+
# @param [Object] value
|
|
47
|
+
RestPositional = Struct.new(:name, :element_type, keyword_init: true)
|
|
48
|
+
|
|
49
|
+
# Simplified representation of an RBS rest-keyword parameter.
|
|
50
|
+
#
|
|
51
|
+
# @!attribute name
|
|
52
|
+
# @return [String, nil] parameter name in RBS, if present
|
|
53
|
+
# @!attribute type
|
|
54
|
+
# @return [String] formatted kwargs type
|
|
55
|
+
#
|
|
56
|
+
# @!attribute [rw] name
|
|
57
|
+
# @return [Object]
|
|
58
|
+
# @param [Object] value
|
|
59
|
+
#
|
|
60
|
+
# @!attribute [rw] type
|
|
61
|
+
# @return [Object]
|
|
62
|
+
# @param [Object] value
|
|
63
|
+
RestKeywords = Struct.new(:name, :type, keyword_init: true)
|
|
64
|
+
end
|
|
65
|
+
end
|