docscribe 1.4.1 → 1.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/README.md +588 -104
- data/lib/docscribe/cli/check_for_comments.rb +183 -0
- data/lib/docscribe/cli/config_builder.rb +180 -36
- data/lib/docscribe/cli/formatters/json.rb +294 -0
- data/lib/docscribe/cli/formatters/sarif.rb +235 -0
- data/lib/docscribe/cli/formatters/text.rb +208 -0
- data/lib/docscribe/cli/formatters.rb +26 -0
- data/lib/docscribe/cli/generate.rb +296 -125
- data/lib/docscribe/cli/init.rb +58 -14
- data/lib/docscribe/cli/options.rb +410 -133
- data/lib/docscribe/cli/rbs_gen.rb +529 -0
- data/lib/docscribe/cli/run.rb +503 -189
- data/lib/docscribe/cli/sigs.rb +366 -0
- data/lib/docscribe/cli/update_types.rb +103 -0
- data/lib/docscribe/cli.rb +35 -9
- data/lib/docscribe/config/defaults.rb +16 -12
- data/lib/docscribe/config/emit.rb +18 -0
- data/lib/docscribe/config/filtering.rb +37 -31
- data/lib/docscribe/config/loader.rb +20 -13
- data/lib/docscribe/config/plugin.rb +2 -1
- data/lib/docscribe/config/rbs.rb +68 -27
- data/lib/docscribe/config/sorbet.rb +40 -17
- data/lib/docscribe/config/sorting.rb +2 -1
- data/lib/docscribe/config/template.rb +10 -1
- data/lib/docscribe/config/utils.rb +12 -9
- data/lib/docscribe/config.rb +3 -4
- data/lib/docscribe/infer/ast_walk.rb +1 -1
- data/lib/docscribe/infer/constants.rb +15 -0
- data/lib/docscribe/infer/literals.rb +39 -26
- data/lib/docscribe/infer/names.rb +24 -16
- data/lib/docscribe/infer/params.rb +57 -13
- data/lib/docscribe/infer/raises.rb +23 -15
- data/lib/docscribe/infer/returns.rb +784 -199
- data/lib/docscribe/infer.rb +28 -28
- data/lib/docscribe/inline_rewriter/collector.rb +816 -430
- data/lib/docscribe/inline_rewriter/doc_block.rb +323 -150
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1837 -648
- data/lib/docscribe/inline_rewriter/source_helpers.rb +119 -71
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +165 -107
- data/lib/docscribe/inline_rewriter.rb +1144 -727
- data/lib/docscribe/parsing.rb +29 -10
- data/lib/docscribe/plugin/base/collector_plugin.rb +3 -3
- data/lib/docscribe/plugin/base/tag_plugin.rb +1 -2
- data/lib/docscribe/plugin/context.rb +28 -18
- data/lib/docscribe/plugin/registry.rb +49 -23
- data/lib/docscribe/plugin/tag.rb +9 -14
- data/lib/docscribe/plugin.rb +54 -22
- data/lib/docscribe/types/provider_chain.rb +4 -2
- data/lib/docscribe/types/rbs/collection_loader.rb +2 -3
- data/lib/docscribe/types/rbs/provider.rb +127 -62
- data/lib/docscribe/types/rbs/type_formatter.rb +286 -77
- data/lib/docscribe/types/signature.rb +22 -42
- data/lib/docscribe/types/sorbet/base_provider.rb +51 -27
- data/lib/docscribe/types/sorbet/rbi_provider.rb +3 -3
- data/lib/docscribe/types/sorbet/source_provider.rb +3 -2
- data/lib/docscribe/types/yard/formatter.rb +100 -0
- data/lib/docscribe/types/yard/parser.rb +240 -0
- data/lib/docscribe/types/yard/types.rb +52 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +34 -2
|
@@ -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
|
#
|
|
@@ -8,18 +9,10 @@ module Docscribe
|
|
|
8
9
|
# Exclude rules win. If no include rules are configured, files are included by default.
|
|
9
10
|
#
|
|
10
11
|
# @param [String] path file path to test
|
|
11
|
-
# @raise [StandardError]
|
|
12
12
|
# @return [Boolean]
|
|
13
13
|
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
|
|
14
|
+
include_patterns, exclude_patterns = load_file_patterns
|
|
15
|
+
rel = relative_path(path)
|
|
23
16
|
|
|
24
17
|
return false if file_matches_any?(exclude_patterns, rel)
|
|
25
18
|
return true if include_patterns.empty?
|
|
@@ -27,6 +20,26 @@ module Docscribe
|
|
|
27
20
|
file_matches_any?(include_patterns, rel)
|
|
28
21
|
end
|
|
29
22
|
|
|
23
|
+
# Load normalized file include/exclude patterns from config.
|
|
24
|
+
#
|
|
25
|
+
# @return [(Array<String>, Array<String>)]
|
|
26
|
+
def load_file_patterns
|
|
27
|
+
files = raw.dig('filter', 'files') || {}
|
|
28
|
+
[normalize_file_patterns(files['include']), normalize_file_patterns(files['exclude'])]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Compute the relative path for filtering.
|
|
32
|
+
#
|
|
33
|
+
# @param [String] path file path to test
|
|
34
|
+
# @raise [StandardError]
|
|
35
|
+
# @return [String] if StandardError
|
|
36
|
+
# @return [Object] if StandardError
|
|
37
|
+
def relative_path(path)
|
|
38
|
+
Pathname.new(path).expand_path.relative_path_from(Pathname.pwd).cleanpath.to_s
|
|
39
|
+
rescue StandardError
|
|
40
|
+
path
|
|
41
|
+
end
|
|
42
|
+
|
|
30
43
|
# Decide whether a method should be processed based on configured method filters.
|
|
31
44
|
#
|
|
32
45
|
# Method IDs are normalized as:
|
|
@@ -55,16 +68,13 @@ module Docscribe
|
|
|
55
68
|
matches_any?(inc, method_id)
|
|
56
69
|
end
|
|
57
70
|
|
|
58
|
-
private
|
|
59
|
-
|
|
60
71
|
# Normalize file filter patterns:
|
|
61
72
|
# - compact nils
|
|
62
73
|
# - stringify
|
|
63
74
|
# - remove empties
|
|
64
75
|
# - expand shorthand directory forms
|
|
65
76
|
#
|
|
66
|
-
# @
|
|
67
|
-
# @param [Array<String>, nil] list raw pattern list
|
|
77
|
+
# @param [Array<String>?] list raw pattern list
|
|
68
78
|
# @return [Array<String>]
|
|
69
79
|
def normalize_file_patterns(list)
|
|
70
80
|
Array(list).compact.map(&:to_s).reject(&:empty?).flat_map { |pat| expand_directory_shorthand(pat) }.uniq
|
|
@@ -76,8 +86,7 @@ module Docscribe
|
|
|
76
86
|
# - `"spec/"` => `"spec/**/*"`
|
|
77
87
|
# - `"spec"` => `"spec/**/*"` if `spec` exists as a directory
|
|
78
88
|
#
|
|
79
|
-
# @
|
|
80
|
-
# @param [String] pattern
|
|
89
|
+
# @param [String] pattern file pattern to expand
|
|
81
90
|
# @return [Array<String>]
|
|
82
91
|
def expand_directory_shorthand(pattern)
|
|
83
92
|
pat = pattern.dup
|
|
@@ -93,9 +102,8 @@ module Docscribe
|
|
|
93
102
|
|
|
94
103
|
# Check whether a file path matches any configured file pattern.
|
|
95
104
|
#
|
|
96
|
-
# @
|
|
97
|
-
# @param [
|
|
98
|
-
# @param [String] path
|
|
105
|
+
# @param [Array<String>] patterns file filter patterns
|
|
106
|
+
# @param [String] path file path to test
|
|
99
107
|
# @return [Boolean]
|
|
100
108
|
def file_matches_any?(patterns, path)
|
|
101
109
|
patterns.any? { |pat| file_match_pattern?(pat, path) }
|
|
@@ -108,13 +116,12 @@ module Docscribe
|
|
|
108
116
|
# - globs
|
|
109
117
|
# - recursive glob shorthand normalization
|
|
110
118
|
#
|
|
111
|
-
# @
|
|
112
|
-
# @param [String]
|
|
113
|
-
# @param [String] path
|
|
119
|
+
# @param [String] pattern file filter pattern
|
|
120
|
+
# @param [String] path file path to test
|
|
114
121
|
# @return [Boolean]
|
|
115
122
|
def file_match_pattern?(pattern, path)
|
|
116
123
|
if pattern.start_with?('/') && pattern.end_with?('/') && pattern.length >= 2
|
|
117
|
-
return Regexp.new(pattern[1..-2]).match?(path)
|
|
124
|
+
return Regexp.new(pattern[1..-2]).match?(path) # steep:ignore
|
|
118
125
|
end
|
|
119
126
|
|
|
120
127
|
patterns_to_try = [pattern]
|
|
@@ -127,34 +134,33 @@ module Docscribe
|
|
|
127
134
|
|
|
128
135
|
# Allowed method scopes from config/defaults.
|
|
129
136
|
#
|
|
130
|
-
# @private
|
|
131
137
|
# @return [Array<String>]
|
|
132
138
|
def filter_scopes
|
|
133
|
-
Array(raw.dig('filter', 'scopes') || DEFAULT.dig('filter', 'scopes')).map(&:to_s)
|
|
139
|
+
Array(raw.dig('filter', 'scopes') || DEFAULT.dig('filter', 'scopes')).map(&:to_s) # steep:ignore
|
|
134
140
|
end
|
|
135
141
|
|
|
136
142
|
# Allowed method visibilities from config/defaults.
|
|
137
143
|
#
|
|
138
|
-
# @private
|
|
139
144
|
# @return [Array<String>]
|
|
140
145
|
def filter_visibilities
|
|
141
|
-
Array(raw.dig('filter', 'visibilities') ||
|
|
146
|
+
Array(raw.dig('filter', 'visibilities') ||
|
|
147
|
+
DEFAULT.dig('filter', 'visibilities')).map(&:to_s) # steep:ignore
|
|
142
148
|
end
|
|
143
149
|
|
|
144
150
|
# Exclude method filter patterns.
|
|
145
151
|
#
|
|
146
|
-
# @private
|
|
147
152
|
# @return [Array<String>]
|
|
148
153
|
def filter_exclude_patterns
|
|
149
|
-
Array(raw.dig('filter', 'exclude') ||
|
|
154
|
+
Array(raw.dig('filter', 'exclude') ||
|
|
155
|
+
DEFAULT.dig('filter', 'exclude')).map(&:to_s).reject(&:empty?) # steep:ignore
|
|
150
156
|
end
|
|
151
157
|
|
|
152
158
|
# Include method filter patterns.
|
|
153
159
|
#
|
|
154
|
-
# @private
|
|
155
160
|
# @return [Array<String>]
|
|
156
161
|
def filter_include_patterns
|
|
157
|
-
Array(raw.dig('filter', 'include') ||
|
|
162
|
+
Array(raw.dig('filter', 'include') ||
|
|
163
|
+
DEFAULT.dig('filter', 'include')).map(&:to_s).reject(&:empty?) # steep:ignore
|
|
158
164
|
end
|
|
159
165
|
end
|
|
160
166
|
end
|
|
@@ -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
|
#
|
|
@@ -9,10 +10,10 @@ module Docscribe
|
|
|
9
10
|
# - `docscribe.yml` in the current directory, if present
|
|
10
11
|
# - otherwise defaults only
|
|
11
12
|
#
|
|
12
|
-
# @param [String
|
|
13
|
+
# @param [String?] 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')
|
|
@@ -27,33 +28,39 @@ module Docscribe
|
|
|
27
28
|
# and calling {safe_load_compat}.
|
|
28
29
|
#
|
|
29
30
|
# @param [String] path file path
|
|
30
|
-
# @return [Hash]
|
|
31
|
+
# @return [Hash<String, Object>]
|
|
31
32
|
def self.safe_load_file_compat(path)
|
|
32
|
-
if YAML.respond_to?(:safe_load_file)
|
|
33
|
-
|
|
33
|
+
if YAML.respond_to?(:safe_load_file) # steep:ignore
|
|
34
|
+
pclasses = [] #: Array[String]
|
|
35
|
+
psymbols = [] #: Array[Symbol]
|
|
36
|
+
YAML.safe_load_file(path, # steep:ignore
|
|
37
|
+
permitted_classes: pclasses, permitted_symbols: psymbols,
|
|
38
|
+
aliases: true) || {} #: Hash[String, untyped]
|
|
34
39
|
else
|
|
35
40
|
yaml = File.open(path, 'r:bom|utf-8', &:read)
|
|
36
|
-
safe_load_compat(yaml, filename: path) || {}
|
|
41
|
+
safe_load_compat(yaml, filename: path) || {} #: Hash[String, untyped]
|
|
37
42
|
end
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
# Safely load YAML from a string across Psych API versions.
|
|
41
46
|
#
|
|
42
47
|
# @param [String] yaml YAML document
|
|
43
|
-
# @param [String
|
|
48
|
+
# @param [String?] filename optional filename for diagnostics
|
|
44
49
|
# @raise [ArgumentError]
|
|
45
|
-
# @return [Hash]
|
|
50
|
+
# @return [Hash<String, Object>] if ArgumentError
|
|
51
|
+
# @return [Object] if ArgumentError
|
|
46
52
|
def self.safe_load_compat(yaml, filename: nil)
|
|
47
|
-
|
|
53
|
+
pclasses = [] #: Array[String]
|
|
54
|
+
psymbols = [] #: Array[Symbol]
|
|
55
|
+
Psych.safe_load( # steep:ignore
|
|
48
56
|
yaml,
|
|
49
|
-
permitted_classes:
|
|
50
|
-
permitted_symbols: [],
|
|
57
|
+
permitted_classes: pclasses, permitted_symbols: psymbols,
|
|
51
58
|
aliases: true,
|
|
52
59
|
filename: filename
|
|
53
|
-
)
|
|
60
|
+
) #: Hash[String, untyped]
|
|
54
61
|
rescue ArgumentError
|
|
55
62
|
# Older Psych signature uses positional args
|
|
56
|
-
Psych.safe_load(yaml, [], [], true, filename)
|
|
63
|
+
Psych.safe_load(yaml, [], [], true, filename) # steep:ignore
|
|
57
64
|
end
|
|
58
65
|
end
|
|
59
66
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# Plugin loading and registration from config.
|
|
4
5
|
class Config
|
|
5
6
|
# Load and register plugins declared under `plugins.require` in config.
|
|
6
7
|
#
|
|
@@ -14,7 +15,7 @@ module Docscribe
|
|
|
14
15
|
# @raise [LoadError]
|
|
15
16
|
# @return [void]
|
|
16
17
|
def load_plugins!
|
|
17
|
-
paths = Array(raw.dig('plugins', 'require')).compact
|
|
18
|
+
paths = Array(raw.dig('plugins', 'require')).compact #: Array[String]
|
|
18
19
|
return if paths.empty?
|
|
19
20
|
|
|
20
21
|
require 'docscribe/plugin'
|
data/lib/docscribe/config/rbs.rb
CHANGED
|
@@ -1,28 +1,19 @@
|
|
|
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
|
#
|
|
7
8
|
# If RBS cannot be loaded, this returns nil and Docscribe falls back to
|
|
8
9
|
# inference.
|
|
9
10
|
#
|
|
10
|
-
# @raise [LoadError]
|
|
11
11
|
# @return [Docscribe::Types::RBS::Provider, nil]
|
|
12
12
|
def rbs_provider
|
|
13
13
|
return nil unless rbs_enabled?
|
|
14
14
|
return nil unless ruby_supports_rbs?
|
|
15
15
|
|
|
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
|
|
16
|
+
@rbs_provider ||= build_rbs_provider
|
|
26
17
|
end
|
|
27
18
|
|
|
28
19
|
# Whether RBS integration is enabled.
|
|
@@ -32,27 +23,28 @@ module Docscribe
|
|
|
32
23
|
fetch_bool(%w[rbs enabled], false)
|
|
33
24
|
end
|
|
34
25
|
|
|
35
|
-
#
|
|
26
|
+
# Core rbs provider
|
|
36
27
|
#
|
|
37
|
-
# @
|
|
38
|
-
# @return [Object]
|
|
28
|
+
# @return [Docscribe::Types::RBS::Provider, nil]
|
|
39
29
|
def core_rbs_provider
|
|
40
30
|
return nil unless ruby_supports_rbs?
|
|
41
31
|
|
|
42
|
-
@core_rbs_provider ||=
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
32
|
+
@core_rbs_provider ||= build_core_rbs_provider
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Whether to warn when rbs_collection.lock.yaml exists but --rbs-collection
|
|
36
|
+
# was not passed.
|
|
37
|
+
#
|
|
38
|
+
# Set `rbs.warn_missing_collection: false` in `docscribe.yml` to suppress.
|
|
39
|
+
#
|
|
40
|
+
# @return [Boolean]
|
|
41
|
+
def rbs_warn_missing_collection?
|
|
42
|
+
fetch_bool(%w[rbs warn_missing_collection], true)
|
|
51
43
|
end
|
|
52
44
|
|
|
53
45
|
private
|
|
54
46
|
|
|
55
|
-
#
|
|
47
|
+
# Check whether the current Ruby version supports RBS (requires 3.0+).
|
|
56
48
|
#
|
|
57
49
|
# @private
|
|
58
50
|
# @return [Boolean]
|
|
@@ -66,12 +58,47 @@ module Docscribe
|
|
|
66
58
|
false
|
|
67
59
|
end
|
|
68
60
|
|
|
61
|
+
# Build rbs provider
|
|
62
|
+
#
|
|
63
|
+
# @private
|
|
64
|
+
# @raise [LoadError]
|
|
65
|
+
# @return [Docscribe::Types::RBS::Provider, nil] if LoadError
|
|
66
|
+
# @return [nil] if LoadError
|
|
67
|
+
def build_rbs_provider
|
|
68
|
+
require 'docscribe/types/rbs/provider'
|
|
69
|
+
Docscribe::Types::RBS::Provider.new(
|
|
70
|
+
sig_dirs: rbs_sig_dirs,
|
|
71
|
+
collection_dirs: rbs_collection_dirs,
|
|
72
|
+
collapse_generics: rbs_collapse_generics?,
|
|
73
|
+
collapse_object_generics: rbs_collapse_object_generics?
|
|
74
|
+
)
|
|
75
|
+
rescue LoadError
|
|
76
|
+
warn 'Docscribe: --rbs requires the `rbs` gem. Add `gem "rbs"` to your Gemfile and run `bundle install`.'
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Build core rbs provider
|
|
81
|
+
#
|
|
82
|
+
# @private
|
|
83
|
+
# @raise [LoadError]
|
|
84
|
+
# @return [Docscribe::Types::RBS::Provider, nil] if LoadError
|
|
85
|
+
# @return [nil] if LoadError
|
|
86
|
+
def build_core_rbs_provider
|
|
87
|
+
require 'docscribe/types/rbs/provider'
|
|
88
|
+
Docscribe::Types::RBS::Provider.new(
|
|
89
|
+
sig_dirs: [],
|
|
90
|
+
collapse_generics: false
|
|
91
|
+
)
|
|
92
|
+
rescue LoadError
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
69
96
|
# Signature directories used by the RBS provider.
|
|
70
97
|
#
|
|
71
98
|
# @private
|
|
72
99
|
# @return [Array<String>]
|
|
73
100
|
def rbs_sig_dirs
|
|
74
|
-
Array(raw.dig('rbs', 'sig_dirs') || DEFAULT.dig('rbs', 'sig_dirs')).map(&:to_s)
|
|
101
|
+
Array(raw.dig('rbs', 'sig_dirs') || DEFAULT.dig('rbs', 'sig_dirs')).map(&:to_s) # steep:ignore
|
|
75
102
|
end
|
|
76
103
|
|
|
77
104
|
# RBS collection directories (auto-discovered from rbs_collection.lock.yaml).
|
|
@@ -83,7 +110,7 @@ module Docscribe
|
|
|
83
110
|
# @private
|
|
84
111
|
# @return [Array<String>]
|
|
85
112
|
def rbs_collection_dirs
|
|
86
|
-
Array(raw.dig('rbs', 'collection_dirs')).map(&:to_s)
|
|
113
|
+
Array(raw.dig('rbs', 'collection_dirs')).map(&:to_s) # steep:ignore
|
|
87
114
|
end
|
|
88
115
|
|
|
89
116
|
# Whether generic RBS types should be collapsed to simpler container names.
|
|
@@ -93,9 +120,23 @@ module Docscribe
|
|
|
93
120
|
# - `Array<Integer>` => `Array`
|
|
94
121
|
#
|
|
95
122
|
# @private
|
|
96
|
-
# @return [
|
|
123
|
+
# @return [Boolean]
|
|
97
124
|
def rbs_collapse_generics?
|
|
98
125
|
fetch_bool(%w[rbs collapse_generics], false)
|
|
99
126
|
end
|
|
127
|
+
|
|
128
|
+
# Whether to collapse generic types when all inner types are Object.
|
|
129
|
+
#
|
|
130
|
+
# Unlike `collapse_generics` (which drops all generic info), this only
|
|
131
|
+
# collapses when the type arguments provide no useful information:
|
|
132
|
+
# - `Hash<Symbol, Object>` => stays as is (Symbol is useful)
|
|
133
|
+
# - `Hash<Object, Object>` => `Hash`
|
|
134
|
+
# - `Array<Object>` => `Array`
|
|
135
|
+
#
|
|
136
|
+
# @private
|
|
137
|
+
# @return [Boolean]
|
|
138
|
+
def rbs_collapse_object_generics?
|
|
139
|
+
fetch_bool(%w[rbs collapse_object_generics], false)
|
|
140
|
+
end
|
|
100
141
|
end
|
|
101
142
|
end
|
|
@@ -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
|
#
|
|
@@ -13,28 +14,50 @@ module Docscribe
|
|
|
13
14
|
#
|
|
14
15
|
# @param [String] source Ruby source being rewritten
|
|
15
16
|
# @param [String] file source name for diagnostics
|
|
16
|
-
# @raise [LoadError]
|
|
17
17
|
# @return [Docscribe::Types::ProviderChain, nil]
|
|
18
18
|
def signature_provider_for(source:, file:)
|
|
19
|
-
providers = []
|
|
19
|
+
providers = [] #: Array[untyped]
|
|
20
|
+
append_sorbet_providers(providers, source: source, file: file)
|
|
21
|
+
providers << rbs_provider if rbs_enabled?
|
|
22
|
+
build_provider_chain(providers)
|
|
23
|
+
end
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
rescue LoadError
|
|
30
|
-
# Sorbet support is optional; fall back quietly.
|
|
31
|
-
end
|
|
25
|
+
# Append Sorbet-based providers to the list.
|
|
26
|
+
#
|
|
27
|
+
# @param [Array<Object>] providers provider list to populate
|
|
28
|
+
# @param [String] source Ruby source being rewritten
|
|
29
|
+
# @param [String] file source name for diagnostics
|
|
30
|
+
# @return [void]
|
|
31
|
+
def append_sorbet_providers(providers, source:, file:)
|
|
32
|
+
return unless sorbet_enabled?
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
providers << sorbet_source_provider(source, file)
|
|
35
|
+
providers << sorbet_rbi_provider
|
|
36
|
+
end
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
# Build a Sorbet source provider (inline sigs).
|
|
39
|
+
#
|
|
40
|
+
# @param [String] source Ruby source being rewritten
|
|
41
|
+
# @param [String] file source name for diagnostics
|
|
42
|
+
# @raise [LoadError]
|
|
43
|
+
# @return [Docscribe::Types::Sorbet::SourceProvider, nil] if LoadError
|
|
44
|
+
# @return [nil] if LoadError
|
|
45
|
+
def sorbet_source_provider(source, file)
|
|
46
|
+
require 'docscribe/types/sorbet/source_provider'
|
|
47
|
+
Docscribe::Types::Sorbet::SourceProvider.new(
|
|
48
|
+
source: source,
|
|
49
|
+
file: file,
|
|
50
|
+
collapse_generics: sorbet_collapse_generics?
|
|
51
|
+
)
|
|
52
|
+
rescue LoadError
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
37
55
|
|
|
56
|
+
# Build the provider chain from a non-empty list, or return nil.
|
|
57
|
+
#
|
|
58
|
+
# @param [Array<Object>] providers provider list to chain
|
|
59
|
+
# @return [Docscribe::Types::ProviderChain, nil]
|
|
60
|
+
def build_provider_chain(providers)
|
|
38
61
|
providers = providers.compact
|
|
39
62
|
return nil if providers.empty?
|
|
40
63
|
|
|
@@ -71,7 +94,7 @@ module Docscribe
|
|
|
71
94
|
#
|
|
72
95
|
# @return [Array<String>]
|
|
73
96
|
def sorbet_rbi_dirs
|
|
74
|
-
Array(raw.dig('sorbet', 'rbi_dirs') || DEFAULT.dig('sorbet', 'rbi_dirs')).map(&:to_s)
|
|
97
|
+
Array(raw.dig('sorbet', 'rbi_dirs') || DEFAULT.dig('sorbet', 'rbi_dirs')).map(&:to_s) # steep:ignore
|
|
75
98
|
end
|
|
76
99
|
|
|
77
100
|
# Whether generic Sorbet/RBI container types should be simplified.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# Tag sorting configuration.
|
|
4
5
|
class Config
|
|
5
6
|
# Whether sortable tag normalization is enabled for doc-like blocks.
|
|
6
7
|
#
|
|
@@ -16,7 +17,7 @@ module Docscribe
|
|
|
16
17
|
# @return [Array<String>]
|
|
17
18
|
def tag_order
|
|
18
19
|
Array(raw.dig('doc', 'tag_order') || DEFAULT.dig('doc', 'tag_order')).map do |t|
|
|
19
|
-
t.to_s.sub(/\A@/, '')
|
|
20
|
+
t.to_s.sub(/\A@/, '') # steep:ignore
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# Default YAML config template for `docscribe init`.
|
|
4
5
|
class Config
|
|
5
6
|
# Return the default YAML template used by `docscribe init`.
|
|
6
7
|
#
|
|
7
8
|
# The template documents the most common CLI workflows and all supported
|
|
8
9
|
# configuration sections with comments.
|
|
9
|
-
# @see Docscribe::Config::DEFAULT
|
|
10
10
|
#
|
|
11
11
|
# @return [String]
|
|
12
12
|
def self.default_yaml
|
|
@@ -20,6 +20,7 @@ module Docscribe
|
|
|
20
20
|
# bundle exec docscribe lib # check what would change
|
|
21
21
|
# bundle exec docscribe -a lib # apply safe updates
|
|
22
22
|
# bundle exec docscribe -A lib # rebuild all doc blocks
|
|
23
|
+
# bundle exec docscribe -AkB lib # rebuild, keep descriptions, no boilerplate
|
|
23
24
|
|
|
24
25
|
emit:
|
|
25
26
|
# What to include in generated documentation
|
|
@@ -88,7 +89,9 @@ module Docscribe
|
|
|
88
89
|
sig_dirs: ["sig"]
|
|
89
90
|
collection_dirs: [] # auto-discovered from --rbs-collection
|
|
90
91
|
collapse_generics: false # Hash<Symbol, String> => Hash
|
|
92
|
+
collapse_object_generics: false # Hash<Object, Object> => Hash (keep if inner types are useful)
|
|
91
93
|
collection: false # auto-discover from rbs_collection.lock.yaml
|
|
94
|
+
warn_missing_collection: true # warn if rbs_collection.lock.yaml exists without --rbs-collection
|
|
92
95
|
|
|
93
96
|
sorbet:
|
|
94
97
|
# Use Sorbet inline sigs and RBI files for better types
|
|
@@ -96,6 +99,12 @@ module Docscribe
|
|
|
96
99
|
rbi_dirs: ["sorbet/rbi", "rbi"]
|
|
97
100
|
collapse_generics: false
|
|
98
101
|
|
|
102
|
+
# Preserve existing @param/@return descriptions in aggressive mode
|
|
103
|
+
keep_descriptions: false
|
|
104
|
+
|
|
105
|
+
# Skip @param for anonymous block arguments (&) (Ruby 3.2+)
|
|
106
|
+
skip_anonymous_block_params: false
|
|
107
|
+
|
|
99
108
|
plugins:
|
|
100
109
|
# Load custom plugins
|
|
101
110
|
# Example:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# Internal config utility methods.
|
|
4
5
|
class Config
|
|
5
6
|
private
|
|
6
7
|
|
|
@@ -45,7 +46,7 @@ module Docscribe
|
|
|
45
46
|
# Convert an internal scope symbol into the config key used under `methods`.
|
|
46
47
|
#
|
|
47
48
|
# @private
|
|
48
|
-
# @param [Symbol] scope
|
|
49
|
+
# @param [Symbol] scope :instance or :class
|
|
49
50
|
# @return [String]
|
|
50
51
|
def scope_to_key(scope)
|
|
51
52
|
scope == :class ? 'class' : 'instance'
|
|
@@ -54,8 +55,8 @@ module Docscribe
|
|
|
54
55
|
# Check whether any pattern matches the given text.
|
|
55
56
|
#
|
|
56
57
|
# @private
|
|
57
|
-
# @param [Array<String>] patterns
|
|
58
|
-
# @param [String] text
|
|
58
|
+
# @param [Array<String>] patterns filter patterns to match
|
|
59
|
+
# @param [String] text text to test against patterns
|
|
59
60
|
# @return [Boolean]
|
|
60
61
|
def matches_any?(patterns, text)
|
|
61
62
|
patterns.any? { |pat| match_pattern?(pat, text) }
|
|
@@ -68,12 +69,14 @@ module Docscribe
|
|
|
68
69
|
# - shell-style glob patterns (with `/` translated to `#` since method IDs use `#`)
|
|
69
70
|
#
|
|
70
71
|
# @private
|
|
71
|
-
# @param [String] pattern
|
|
72
|
-
# @param [String] text
|
|
72
|
+
# @param [String] pattern filter pattern to match
|
|
73
|
+
# @param [String] text method ID to test
|
|
73
74
|
# @return [Boolean]
|
|
74
75
|
def match_pattern?(pattern, text)
|
|
75
76
|
if pattern.start_with?('/') && pattern.end_with?('/') && pattern.length >= 2
|
|
76
|
-
Regexp.new(pattern[1..-2]).match?(text)
|
|
77
|
+
Regexp.new(pattern[1..-2]).match?(text) # steep:ignore
|
|
78
|
+
elsif pattern.count('*?[{').zero?
|
|
79
|
+
File.fnmatch?("*#{pattern.tr('/', '#')}*", text, File::FNM_EXTGLOB)
|
|
77
80
|
else
|
|
78
81
|
File.fnmatch?(pattern.tr('/', '#'), text, File::FNM_EXTGLOB)
|
|
79
82
|
end
|
|
@@ -84,9 +87,9 @@ module Docscribe
|
|
|
84
87
|
# Nested hashes are merged recursively; non-hash values are replaced.
|
|
85
88
|
#
|
|
86
89
|
# @private
|
|
87
|
-
# @param [Hash] hash1 base hash
|
|
88
|
-
# @param [Hash, nil] hash2 override hash
|
|
89
|
-
# @return [Hash]
|
|
90
|
+
# @param [Hash<Object, Object>] hash1 base hash
|
|
91
|
+
# @param [Hash<Object, Object>, nil] hash2 override hash
|
|
92
|
+
# @return [Hash<Object, Object>]
|
|
90
93
|
def deep_merge(hash1, hash2)
|
|
91
94
|
return hash1 unless hash2
|
|
92
95
|
|
data/lib/docscribe/config.rb
CHANGED
|
@@ -5,18 +5,17 @@ require 'pathname'
|
|
|
5
5
|
require 'psych'
|
|
6
6
|
|
|
7
7
|
module Docscribe
|
|
8
|
+
# Application configuration with deep-merge defaults and overrides.
|
|
8
9
|
class Config
|
|
9
|
-
# Raw config hash after deep-merging user config with defaults.
|
|
10
|
-
#
|
|
11
10
|
# @!attribute [r] raw
|
|
12
|
-
# @return [Hash]
|
|
11
|
+
# @return [Hash<String, Object>]
|
|
13
12
|
attr_reader :raw
|
|
14
13
|
|
|
15
14
|
# Create a configuration object from a raw config hash.
|
|
16
15
|
#
|
|
17
16
|
# Missing keys are filled from {DEFAULT} via deep merge.
|
|
18
17
|
#
|
|
19
|
-
# @param [Hash,
|
|
18
|
+
# @param [Hash<String, Object>] raw user-provided config hash
|
|
20
19
|
# @return [void]
|
|
21
20
|
def initialize(raw = {})
|
|
22
21
|
@raw = deep_merge(DEFAULT, raw || {})
|
|
@@ -11,7 +11,7 @@ module Docscribe
|
|
|
11
11
|
# Yields each node exactly once, descending recursively through child nodes.
|
|
12
12
|
# Non-AST values are ignored.
|
|
13
13
|
#
|
|
14
|
-
# @note module_function:
|
|
14
|
+
# @note module_function: defines #walk (visibility: private)
|
|
15
15
|
# @param [Parser::AST::Node, nil] node root AST node
|
|
16
16
|
# @param [Proc] block visitor block
|
|
17
17
|
# @return [void]
|
|
@@ -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
|