docscribe 1.4.1 → 1.4.2
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 +149 -0
- data/lib/docscribe/cli/config_builder.rb +125 -35
- data/lib/docscribe/cli/generate.rb +288 -117
- data/lib/docscribe/cli/init.rb +49 -13
- data/lib/docscribe/cli/options.rb +302 -127
- data/lib/docscribe/cli/run.rb +391 -135
- data/lib/docscribe/cli.rb +23 -5
- data/lib/docscribe/config/defaults.rb +11 -11
- data/lib/docscribe/config/emit.rb +1 -0
- data/lib/docscribe/config/filtering.rb +24 -11
- data/lib/docscribe/config/loader.rb +7 -4
- data/lib/docscribe/config/plugin.rb +1 -0
- data/lib/docscribe/config/rbs.rb +31 -22
- data/lib/docscribe/config/sorbet.rb +41 -15
- data/lib/docscribe/config/sorting.rb +1 -0
- data/lib/docscribe/config/template.rb +1 -0
- data/lib/docscribe/config/utils.rb +1 -0
- data/lib/docscribe/config.rb +1 -0
- data/lib/docscribe/infer/constants.rb +15 -0
- data/lib/docscribe/infer/literals.rb +43 -25
- data/lib/docscribe/infer/names.rb +24 -15
- data/lib/docscribe/infer/params.rb +52 -6
- data/lib/docscribe/infer/raises.rb +24 -14
- data/lib/docscribe/infer/returns.rb +365 -182
- data/lib/docscribe/infer.rb +10 -9
- data/lib/docscribe/inline_rewriter/collector.rb +766 -375
- data/lib/docscribe/inline_rewriter/doc_block.rb +217 -74
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1488 -602
- data/lib/docscribe/inline_rewriter/source_helpers.rb +100 -52
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +109 -48
- data/lib/docscribe/inline_rewriter.rb +1009 -595
- data/lib/docscribe/plugin/base/collector_plugin.rb +2 -3
- data/lib/docscribe/plugin/base/tag_plugin.rb +1 -1
- data/lib/docscribe/plugin/registry.rb +34 -7
- data/lib/docscribe/plugin.rb +48 -17
- data/lib/docscribe/types/rbs/collection_loader.rb +0 -1
- data/lib/docscribe/types/rbs/provider.rb +75 -26
- data/lib/docscribe/types/rbs/type_formatter.rb +127 -59
- data/lib/docscribe/types/sorbet/base_provider.rb +31 -12
- data/lib/docscribe/version.rb +1 -1
- metadata +2 -2
data/lib/docscribe/cli.rb
CHANGED
|
@@ -6,6 +6,7 @@ require 'docscribe/cli/options'
|
|
|
6
6
|
require 'docscribe/cli/run'
|
|
7
7
|
|
|
8
8
|
module Docscribe
|
|
9
|
+
# CLI entry point and command dispatch.
|
|
9
10
|
module CLI
|
|
10
11
|
class << self
|
|
11
12
|
# Main CLI entry point.
|
|
@@ -19,18 +20,35 @@ module Docscribe
|
|
|
19
20
|
# @return [Integer] process exit code
|
|
20
21
|
def run(argv)
|
|
21
22
|
argv = argv.dup
|
|
23
|
+
return dispatch_subcommand(argv) if subcommand?(argv.first)
|
|
22
24
|
|
|
25
|
+
options = Docscribe::CLI::Options.parse!(argv)
|
|
26
|
+
Docscribe::CLI::Run.run(options: options, argv: argv)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# @private
|
|
32
|
+
# @param [String] cmd
|
|
33
|
+
# @return [Boolean]
|
|
34
|
+
def subcommand?(cmd)
|
|
35
|
+
%w[init generate].include?(cmd)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @private
|
|
39
|
+
# @param [Array<String>] argv
|
|
40
|
+
# @return [Integer, nil]
|
|
41
|
+
def dispatch_subcommand(argv)
|
|
23
42
|
case argv.first
|
|
24
43
|
when 'init'
|
|
25
44
|
argv.shift
|
|
26
|
-
|
|
45
|
+
Docscribe::CLI::Init.run(argv)
|
|
27
46
|
when 'generate'
|
|
28
47
|
argv.shift
|
|
29
|
-
|
|
48
|
+
Docscribe::CLI::Generate.run(argv)
|
|
49
|
+
else
|
|
50
|
+
0
|
|
30
51
|
end
|
|
31
|
-
|
|
32
|
-
options = Docscribe::CLI::Options.parse!(argv)
|
|
33
|
-
Docscribe::CLI::Run.run(options: options, argv: argv)
|
|
34
52
|
end
|
|
35
53
|
end
|
|
36
54
|
end
|
|
@@ -33,14 +33,14 @@ module Docscribe
|
|
|
33
33
|
},
|
|
34
34
|
'methods' => {
|
|
35
35
|
'instance' => {
|
|
36
|
-
'public' => {},
|
|
37
|
-
'protected' => {},
|
|
38
|
-
'private' => {}
|
|
36
|
+
'public' => {}, #: Hash[String, untyped]
|
|
37
|
+
'protected' => {}, #: Hash[String, untyped]
|
|
38
|
+
'private' => {} #: Hash[String, untyped]
|
|
39
39
|
},
|
|
40
40
|
'class' => {
|
|
41
|
-
'public' => {},
|
|
42
|
-
'protected' => {},
|
|
43
|
-
'private' => {}
|
|
41
|
+
'public' => {}, #: Hash[String, untyped]
|
|
42
|
+
'protected' => {}, #: Hash[String, untyped]
|
|
43
|
+
'private' => {} #: Hash[String, untyped]
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
'inference' => {
|
|
@@ -51,10 +51,10 @@ module Docscribe
|
|
|
51
51
|
'filter' => {
|
|
52
52
|
'visibilities' => %w[public protected private],
|
|
53
53
|
'scopes' => %w[instance class],
|
|
54
|
-
'include' => [],
|
|
55
|
-
'exclude' => [],
|
|
54
|
+
'include' => [], #: Array[String]
|
|
55
|
+
'exclude' => [], #: Array[String]
|
|
56
56
|
'files' => {
|
|
57
|
-
'include' => [],
|
|
57
|
+
'include' => [], #: Array[String]
|
|
58
58
|
'exclude' => ['spec']
|
|
59
59
|
}
|
|
60
60
|
},
|
|
@@ -62,7 +62,7 @@ module Docscribe
|
|
|
62
62
|
'enabled' => false,
|
|
63
63
|
'collection' => false,
|
|
64
64
|
'sig_dirs' => ['sig'],
|
|
65
|
-
'collection_dirs' => [],
|
|
65
|
+
'collection_dirs' => [], #: Array[String]
|
|
66
66
|
'collapse_generics' => false
|
|
67
67
|
},
|
|
68
68
|
'sorbet' => {
|
|
@@ -71,7 +71,7 @@ module Docscribe
|
|
|
71
71
|
'collapse_generics' => false
|
|
72
72
|
},
|
|
73
73
|
'plugins' => {
|
|
74
|
-
'require' => []
|
|
74
|
+
'require' => [] #: Array[String]
|
|
75
75
|
}
|
|
76
76
|
}.freeze
|
|
77
77
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# File and method include/exclude filtering.
|
|
4
5
|
class Config
|
|
5
6
|
# Decide whether a file path should be processed based on `filter.files`.
|
|
6
7
|
#
|
|
@@ -11,15 +12,8 @@ module Docscribe
|
|
|
11
12
|
# @raise [StandardError]
|
|
12
13
|
# @return [Boolean]
|
|
13
14
|
def process_file?(path)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
exclude_patterns = normalize_file_patterns(files['exclude'])
|
|
17
|
-
|
|
18
|
-
rel = begin
|
|
19
|
-
Pathname.new(path).expand_path.relative_path_from(Pathname.pwd).cleanpath.to_s
|
|
20
|
-
rescue StandardError
|
|
21
|
-
path
|
|
22
|
-
end
|
|
15
|
+
include_patterns, exclude_patterns = load_file_patterns
|
|
16
|
+
rel = relative_path(path)
|
|
23
17
|
|
|
24
18
|
return false if file_matches_any?(exclude_patterns, rel)
|
|
25
19
|
return true if include_patterns.empty?
|
|
@@ -27,6 +21,27 @@ module Docscribe
|
|
|
27
21
|
file_matches_any?(include_patterns, rel)
|
|
28
22
|
end
|
|
29
23
|
|
|
24
|
+
# Load normalized file include/exclude patterns from config.
|
|
25
|
+
#
|
|
26
|
+
# @private
|
|
27
|
+
# @return [Array(Array<String>, Array<String>)] include_patterns, exclude_patterns
|
|
28
|
+
def load_file_patterns
|
|
29
|
+
files = raw.dig('filter', 'files') || {}
|
|
30
|
+
[normalize_file_patterns(files['include']), normalize_file_patterns(files['exclude'])]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Compute the relative path for filtering.
|
|
34
|
+
#
|
|
35
|
+
# @private
|
|
36
|
+
# @param [String] path
|
|
37
|
+
# @raise [StandardError]
|
|
38
|
+
# @return [String]
|
|
39
|
+
def relative_path(path)
|
|
40
|
+
Pathname.new(path).expand_path.relative_path_from(Pathname.pwd).cleanpath.to_s
|
|
41
|
+
rescue StandardError
|
|
42
|
+
path
|
|
43
|
+
end
|
|
44
|
+
|
|
30
45
|
# Decide whether a method should be processed based on configured method filters.
|
|
31
46
|
#
|
|
32
47
|
# Method IDs are normalized as:
|
|
@@ -55,8 +70,6 @@ module Docscribe
|
|
|
55
70
|
matches_any?(inc, method_id)
|
|
56
71
|
end
|
|
57
72
|
|
|
58
|
-
private
|
|
59
|
-
|
|
60
73
|
# Normalize file filter patterns:
|
|
61
74
|
# - compact nils
|
|
62
75
|
# - stringify
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# YAML config file loading and resolution.
|
|
4
5
|
class Config
|
|
5
6
|
# Load Docscribe configuration from YAML.
|
|
6
7
|
#
|
|
@@ -12,7 +13,7 @@ module Docscribe
|
|
|
12
13
|
# @param [String, nil] path optional config path
|
|
13
14
|
# @return [Docscribe::Config]
|
|
14
15
|
def self.load(path = nil)
|
|
15
|
-
raw = {}
|
|
16
|
+
raw = {} #: Hash[String, untyped]
|
|
16
17
|
if path && File.file?(path)
|
|
17
18
|
raw = safe_load_file_compat(path)
|
|
18
19
|
elsif File.file?('docscribe.yml')
|
|
@@ -30,10 +31,12 @@ module Docscribe
|
|
|
30
31
|
# @return [Hash]
|
|
31
32
|
def self.safe_load_file_compat(path)
|
|
32
33
|
if YAML.respond_to?(:safe_load_file)
|
|
33
|
-
YAML.safe_load_file(path,
|
|
34
|
+
YAML.safe_load_file(path,
|
|
35
|
+
permitted_classes: [], permitted_symbols: [],
|
|
36
|
+
aliases: true) || {} #: Hash[String, untyped]
|
|
34
37
|
else
|
|
35
38
|
yaml = File.open(path, 'r:bom|utf-8', &:read)
|
|
36
|
-
safe_load_compat(yaml, filename: path) || {}
|
|
39
|
+
safe_load_compat(yaml, filename: path) || {} #: Hash[String, untyped]
|
|
37
40
|
end
|
|
38
41
|
end
|
|
39
42
|
|
|
@@ -50,7 +53,7 @@ module Docscribe
|
|
|
50
53
|
permitted_symbols: [],
|
|
51
54
|
aliases: true,
|
|
52
55
|
filename: filename
|
|
53
|
-
)
|
|
56
|
+
) #: Hash[String, untyped]
|
|
54
57
|
rescue ArgumentError
|
|
55
58
|
# Older Psych signature uses positional args
|
|
56
59
|
Psych.safe_load(yaml, [], [], true, filename)
|
data/lib/docscribe/config/rbs.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# RBS signature provider configuration.
|
|
4
5
|
class Config
|
|
5
6
|
# Return a memoized RBS provider if RBS integration is enabled and available.
|
|
6
7
|
#
|
|
@@ -13,16 +14,7 @@ module Docscribe
|
|
|
13
14
|
return nil unless rbs_enabled?
|
|
14
15
|
return nil unless ruby_supports_rbs?
|
|
15
16
|
|
|
16
|
-
@rbs_provider ||=
|
|
17
|
-
require 'docscribe/types/rbs/provider'
|
|
18
|
-
Docscribe::Types::RBS::Provider.new(
|
|
19
|
-
sig_dirs: rbs_sig_dirs,
|
|
20
|
-
collection_dirs: rbs_collection_dirs,
|
|
21
|
-
collapse_generics: rbs_collapse_generics?
|
|
22
|
-
)
|
|
23
|
-
rescue LoadError
|
|
24
|
-
nil
|
|
25
|
-
end
|
|
17
|
+
@rbs_provider ||= build_rbs_provider
|
|
26
18
|
end
|
|
27
19
|
|
|
28
20
|
# Whether RBS integration is enabled.
|
|
@@ -32,27 +24,17 @@ module Docscribe
|
|
|
32
24
|
fetch_bool(%w[rbs enabled], false)
|
|
33
25
|
end
|
|
34
26
|
|
|
35
|
-
# Method documentation.
|
|
36
|
-
#
|
|
37
27
|
# @raise [LoadError]
|
|
38
28
|
# @return [Object]
|
|
39
29
|
def core_rbs_provider
|
|
40
30
|
return nil unless ruby_supports_rbs?
|
|
41
31
|
|
|
42
|
-
@core_rbs_provider ||=
|
|
43
|
-
require 'docscribe/types/rbs/provider'
|
|
44
|
-
Docscribe::Types::RBS::Provider.new(
|
|
45
|
-
sig_dirs: [],
|
|
46
|
-
collapse_generics: false
|
|
47
|
-
)
|
|
48
|
-
rescue LoadError
|
|
49
|
-
nil
|
|
50
|
-
end
|
|
32
|
+
@core_rbs_provider ||= build_core_rbs_provider
|
|
51
33
|
end
|
|
52
34
|
|
|
53
35
|
private
|
|
54
36
|
|
|
55
|
-
#
|
|
37
|
+
# Check whether the current Ruby version supports RBS (requires 3.0+).
|
|
56
38
|
#
|
|
57
39
|
# @private
|
|
58
40
|
# @return [Boolean]
|
|
@@ -66,6 +48,33 @@ module Docscribe
|
|
|
66
48
|
false
|
|
67
49
|
end
|
|
68
50
|
|
|
51
|
+
# @private
|
|
52
|
+
# @raise [LoadError]
|
|
53
|
+
# @return [Docscribe::Types::RBS::Provider, nil]
|
|
54
|
+
def build_rbs_provider
|
|
55
|
+
require 'docscribe/types/rbs/provider'
|
|
56
|
+
Docscribe::Types::RBS::Provider.new(
|
|
57
|
+
sig_dirs: rbs_sig_dirs,
|
|
58
|
+
collection_dirs: rbs_collection_dirs,
|
|
59
|
+
collapse_generics: rbs_collapse_generics?
|
|
60
|
+
)
|
|
61
|
+
rescue LoadError
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @private
|
|
66
|
+
# @raise [LoadError]
|
|
67
|
+
# @return [Docscribe::Types::RBS::Provider, nil]
|
|
68
|
+
def build_core_rbs_provider
|
|
69
|
+
require 'docscribe/types/rbs/provider'
|
|
70
|
+
Docscribe::Types::RBS::Provider.new(
|
|
71
|
+
sig_dirs: [],
|
|
72
|
+
collapse_generics: false
|
|
73
|
+
)
|
|
74
|
+
rescue LoadError
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
69
78
|
# Signature directories used by the RBS provider.
|
|
70
79
|
#
|
|
71
80
|
# @private
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# Sorbet and signature provider configuration.
|
|
4
5
|
class Config
|
|
5
6
|
# Build the effective external signature provider chain for a given source.
|
|
6
7
|
#
|
|
@@ -16,25 +17,50 @@ module Docscribe
|
|
|
16
17
|
# @raise [LoadError]
|
|
17
18
|
# @return [Docscribe::Types::ProviderChain, nil]
|
|
18
19
|
def signature_provider_for(source:, file:)
|
|
19
|
-
providers = []
|
|
20
|
+
providers = [] #: Array[untyped]
|
|
21
|
+
append_sorbet_providers(providers, source: source, file: file)
|
|
22
|
+
providers << rbs_provider if rbs_enabled?
|
|
23
|
+
build_provider_chain(providers)
|
|
24
|
+
end
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# Sorbet support is optional; fall back quietly.
|
|
31
|
-
end
|
|
26
|
+
# Append Sorbet-based providers to the list.
|
|
27
|
+
#
|
|
28
|
+
# @private
|
|
29
|
+
# @param [Array] providers
|
|
30
|
+
# @param [String] source
|
|
31
|
+
# @param [String] file
|
|
32
|
+
# @return [void]
|
|
33
|
+
def append_sorbet_providers(providers, source:, file:)
|
|
34
|
+
return unless sorbet_enabled?
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
providers << sorbet_source_provider(source, file)
|
|
37
|
+
providers << sorbet_rbi_provider
|
|
38
|
+
end
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
# Build a Sorbet source provider (inline sigs).
|
|
41
|
+
#
|
|
42
|
+
# @private
|
|
43
|
+
# @param [String] source
|
|
44
|
+
# @param [String] file
|
|
45
|
+
# @raise [LoadError]
|
|
46
|
+
# @return [Docscribe::Types::Sorbet::SourceProvider, nil]
|
|
47
|
+
def sorbet_source_provider(source, file)
|
|
48
|
+
require 'docscribe/types/sorbet/source_provider'
|
|
49
|
+
Docscribe::Types::Sorbet::SourceProvider.new(
|
|
50
|
+
source: source,
|
|
51
|
+
file: file,
|
|
52
|
+
collapse_generics: sorbet_collapse_generics?
|
|
53
|
+
)
|
|
54
|
+
rescue LoadError
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
37
57
|
|
|
58
|
+
# Build the provider chain from a non-empty list, or return nil.
|
|
59
|
+
#
|
|
60
|
+
# @private
|
|
61
|
+
# @param [Array] providers
|
|
62
|
+
# @return [Docscribe::Types::ProviderChain, nil]
|
|
63
|
+
def build_provider_chain(providers)
|
|
38
64
|
providers = providers.compact
|
|
39
65
|
return nil if providers.empty?
|
|
40
66
|
|
data/lib/docscribe/config.rb
CHANGED
|
@@ -7,5 +7,20 @@ module Docscribe
|
|
|
7
7
|
|
|
8
8
|
# Ruby's implicit rescue target for bare `rescue`.
|
|
9
9
|
DEFAULT_ERROR = 'StandardError'
|
|
10
|
+
|
|
11
|
+
# Node type to literal type name mapping.
|
|
12
|
+
LITERAL_TYPE_MAP = {
|
|
13
|
+
int: 'Integer',
|
|
14
|
+
float: 'Float',
|
|
15
|
+
str: 'String',
|
|
16
|
+
dstr: 'String',
|
|
17
|
+
sym: 'Symbol',
|
|
18
|
+
true: 'Boolean',
|
|
19
|
+
false: 'Boolean',
|
|
20
|
+
nil: 'nil',
|
|
21
|
+
array: 'Array',
|
|
22
|
+
hash: 'Hash',
|
|
23
|
+
regexp: 'Regexp'
|
|
24
|
+
}.freeze
|
|
10
25
|
end
|
|
11
26
|
end
|
|
@@ -24,31 +24,49 @@ module Docscribe
|
|
|
24
24
|
def type_from_literal(node, fallback_type: FALLBACK_TYPE)
|
|
25
25
|
return fallback_type unless node
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
27
|
+
literal_type_for(node.type) || const_type_for(node, fallback_type) ||
|
|
28
|
+
send_new_type_for(node, fallback_type) || fallback_type
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Map a node type symbol to a known literal type name.
|
|
32
|
+
#
|
|
33
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
34
|
+
# @private
|
|
35
|
+
# @param [Symbol] type node type
|
|
36
|
+
# @return [String, nil]
|
|
37
|
+
def literal_type_for(type)
|
|
38
|
+
LITERAL_TYPE_MAP[type]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Extract a constant name from a `:const` node.
|
|
42
|
+
#
|
|
43
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
44
|
+
# @private
|
|
45
|
+
# @param [Parser::AST::Node] node
|
|
46
|
+
# @param [String] fallback_type
|
|
47
|
+
# @param [String] _fallback_type fallback type string (unused here)
|
|
48
|
+
# @return [String, nil]
|
|
49
|
+
def const_type_for(node, _fallback_type)
|
|
50
|
+
return unless node.type == :const
|
|
51
|
+
|
|
52
|
+
node.children.last.to_s
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Extract a type from a `Foo.new` send node.
|
|
56
|
+
#
|
|
57
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
58
|
+
# @private
|
|
59
|
+
# @param [Parser::AST::Node] node
|
|
60
|
+
# @param [String] fallback_type
|
|
61
|
+
# @param [String] _fallback_type fallback type string (unused here)
|
|
62
|
+
# @return [String, nil]
|
|
63
|
+
def send_new_type_for(node, _fallback_type)
|
|
64
|
+
return unless node.type == :send
|
|
65
|
+
|
|
66
|
+
recv, meth, = node.children
|
|
67
|
+
return unless meth == :new && recv&.type == :const
|
|
68
|
+
|
|
69
|
+
recv.children.last.to_s
|
|
52
70
|
end
|
|
53
71
|
end
|
|
54
72
|
end
|
|
@@ -16,26 +16,35 @@ module Docscribe
|
|
|
16
16
|
# Returns nil for unsupported nodes.
|
|
17
17
|
#
|
|
18
18
|
# @note module_function: when included, also defines #const_full_name (instance visibility: private)
|
|
19
|
-
# @param [Parser::AST::Node, nil]
|
|
19
|
+
# @param [Parser::AST::Node, nil] node constant-like AST node
|
|
20
20
|
# @return [String, nil]
|
|
21
|
-
def const_full_name(
|
|
22
|
-
return nil unless
|
|
21
|
+
def const_full_name(node)
|
|
22
|
+
return nil unless node.is_a?(Parser::AST::Node)
|
|
23
23
|
|
|
24
|
-
case
|
|
24
|
+
case node.type
|
|
25
25
|
when :const
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
build_const_full_name(node)
|
|
27
|
+
when :cbase
|
|
28
|
+
''
|
|
29
|
+
end
|
|
30
|
+
end
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
# Build the fully qualified name from a `:const` node.
|
|
33
|
+
#
|
|
34
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
35
|
+
# @private
|
|
36
|
+
# @param [Parser::AST::Node] node a `:const` node
|
|
37
|
+
# @return [String]
|
|
38
|
+
def build_const_full_name(node)
|
|
39
|
+
scope, name = *node
|
|
40
|
+
scope_name = const_full_name(scope)
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
if scope_name && !scope_name.empty?
|
|
43
|
+
"#{scope_name}::#{name}"
|
|
44
|
+
elsif scope_name == ''
|
|
45
|
+
"::#{name}"
|
|
46
|
+
else
|
|
47
|
+
name.to_s
|
|
39
48
|
end
|
|
40
49
|
end
|
|
41
50
|
end
|
|
@@ -23,23 +23,69 @@ module Docscribe
|
|
|
23
23
|
# be treated specially as Hash
|
|
24
24
|
# @return [String]
|
|
25
25
|
def infer_param_type(name, default_str, fallback_type: FALLBACK_TYPE, treat_options_keyword_as_hash: true)
|
|
26
|
+
prefix_param_type(name) || inferred_param_type(name, default_str, fallback_type,
|
|
27
|
+
treat_options_keyword_as_hash: treat_options_keyword_as_hash)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Return type for special parameter prefixes.
|
|
31
|
+
#
|
|
32
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
33
|
+
# @private
|
|
34
|
+
# @param [String] name parameter name
|
|
35
|
+
# @return [String, nil]
|
|
36
|
+
def prefix_param_type(name)
|
|
26
37
|
return 'Array' if name.start_with?('*') && !name.start_with?('**')
|
|
27
38
|
return 'Hash' if name.start_with?('**')
|
|
28
39
|
return 'Proc' if name.start_with?('&')
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
ty = Literals.type_from_literal(node, fallback_type: fallback_type)
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
# Infer type for a regular or keyword parameter with optional default.
|
|
45
|
+
#
|
|
46
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
47
|
+
# @private
|
|
48
|
+
# @param [String] name parameter name
|
|
49
|
+
# @param [String, nil] default_str default expression source
|
|
50
|
+
# @param [String] fallback_type
|
|
51
|
+
# @param [Boolean] treat_options_keyword_as_hash
|
|
52
|
+
# @return [String]
|
|
53
|
+
def inferred_param_type(name, default_str, fallback_type, treat_options_keyword_as_hash:)
|
|
54
|
+
if name.end_with?(':') && default_str.nil?
|
|
55
|
+
return options_keyword_type(name, treat_options_keyword_as_hash, fallback_type)
|
|
36
56
|
end
|
|
37
57
|
|
|
38
|
-
|
|
58
|
+
node = parse_expr(default_str)
|
|
59
|
+
ty = Literals.type_from_literal(node, fallback_type: fallback_type)
|
|
60
|
+
|
|
61
|
+
return 'Hash' if options_hash_keyword?(name, default_str, ty, treat_options_keyword_as_hash)
|
|
39
62
|
|
|
40
63
|
ty
|
|
41
64
|
end
|
|
42
65
|
|
|
66
|
+
# Return 'Hash' for a keyword parameter named 'options:' when special-cased, else fallback.
|
|
67
|
+
#
|
|
68
|
+
# @note module_function: when included, also defines #options_keyword_type (instance visibility: private)
|
|
69
|
+
# @param [String] name parameter name
|
|
70
|
+
# @param [Boolean] treat_options_keyword_as_hash whether to treat 'options:' as Hash
|
|
71
|
+
# @param [String] fallback_type type returned when not special-cased
|
|
72
|
+
# @return [String]
|
|
73
|
+
def options_keyword_type(name, treat_options_keyword_as_hash, fallback_type)
|
|
74
|
+
treat_options_keyword_as_hash && name == 'options:' ? 'Hash' : fallback_type
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Whether a keyword parameter named 'options:' with a hash default should be typed as Hash.
|
|
78
|
+
#
|
|
79
|
+
# @note module_function: when included, also defines #options_hash_keyword? (instance visibility: private)
|
|
80
|
+
# @param [String] name parameter name
|
|
81
|
+
# @param [String, nil] default_str default expression source
|
|
82
|
+
# @param [String] type inferred type
|
|
83
|
+
# @param [Boolean] treat_options_keyword_as_hash whether to treat 'options:' as Hash
|
|
84
|
+
# @return [Boolean]
|
|
85
|
+
def options_hash_keyword?(name, default_str, type, treat_options_keyword_as_hash)
|
|
86
|
+
treat_options_keyword_as_hash && name == 'options:' && (default_str == '{}' || type == 'Hash')
|
|
87
|
+
end
|
|
88
|
+
|
|
43
89
|
# Parse a standalone expression for parameter-default inference.
|
|
44
90
|
#
|
|
45
91
|
# Returns nil if the expression is empty or cannot be parsed.
|