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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +588 -104
  3. data/lib/docscribe/cli/check_for_comments.rb +183 -0
  4. data/lib/docscribe/cli/config_builder.rb +180 -36
  5. data/lib/docscribe/cli/formatters/json.rb +294 -0
  6. data/lib/docscribe/cli/formatters/sarif.rb +235 -0
  7. data/lib/docscribe/cli/formatters/text.rb +208 -0
  8. data/lib/docscribe/cli/formatters.rb +26 -0
  9. data/lib/docscribe/cli/generate.rb +296 -125
  10. data/lib/docscribe/cli/init.rb +58 -14
  11. data/lib/docscribe/cli/options.rb +410 -133
  12. data/lib/docscribe/cli/rbs_gen.rb +529 -0
  13. data/lib/docscribe/cli/run.rb +503 -189
  14. data/lib/docscribe/cli/sigs.rb +366 -0
  15. data/lib/docscribe/cli/update_types.rb +103 -0
  16. data/lib/docscribe/cli.rb +35 -9
  17. data/lib/docscribe/config/defaults.rb +16 -12
  18. data/lib/docscribe/config/emit.rb +18 -0
  19. data/lib/docscribe/config/filtering.rb +37 -31
  20. data/lib/docscribe/config/loader.rb +20 -13
  21. data/lib/docscribe/config/plugin.rb +2 -1
  22. data/lib/docscribe/config/rbs.rb +68 -27
  23. data/lib/docscribe/config/sorbet.rb +40 -17
  24. data/lib/docscribe/config/sorting.rb +2 -1
  25. data/lib/docscribe/config/template.rb +10 -1
  26. data/lib/docscribe/config/utils.rb +12 -9
  27. data/lib/docscribe/config.rb +3 -4
  28. data/lib/docscribe/infer/ast_walk.rb +1 -1
  29. data/lib/docscribe/infer/constants.rb +15 -0
  30. data/lib/docscribe/infer/literals.rb +39 -26
  31. data/lib/docscribe/infer/names.rb +24 -16
  32. data/lib/docscribe/infer/params.rb +57 -13
  33. data/lib/docscribe/infer/raises.rb +23 -15
  34. data/lib/docscribe/infer/returns.rb +784 -199
  35. data/lib/docscribe/infer.rb +28 -28
  36. data/lib/docscribe/inline_rewriter/collector.rb +816 -430
  37. data/lib/docscribe/inline_rewriter/doc_block.rb +323 -150
  38. data/lib/docscribe/inline_rewriter/doc_builder.rb +1837 -648
  39. data/lib/docscribe/inline_rewriter/source_helpers.rb +119 -71
  40. data/lib/docscribe/inline_rewriter/tag_sorter.rb +165 -107
  41. data/lib/docscribe/inline_rewriter.rb +1144 -727
  42. data/lib/docscribe/parsing.rb +29 -10
  43. data/lib/docscribe/plugin/base/collector_plugin.rb +3 -3
  44. data/lib/docscribe/plugin/base/tag_plugin.rb +1 -2
  45. data/lib/docscribe/plugin/context.rb +28 -18
  46. data/lib/docscribe/plugin/registry.rb +49 -23
  47. data/lib/docscribe/plugin/tag.rb +9 -14
  48. data/lib/docscribe/plugin.rb +54 -22
  49. data/lib/docscribe/types/provider_chain.rb +4 -2
  50. data/lib/docscribe/types/rbs/collection_loader.rb +2 -3
  51. data/lib/docscribe/types/rbs/provider.rb +127 -62
  52. data/lib/docscribe/types/rbs/type_formatter.rb +286 -77
  53. data/lib/docscribe/types/signature.rb +22 -42
  54. data/lib/docscribe/types/sorbet/base_provider.rb +51 -27
  55. data/lib/docscribe/types/sorbet/rbi_provider.rb +3 -3
  56. data/lib/docscribe/types/sorbet/source_provider.rb +3 -2
  57. data/lib/docscribe/types/yard/formatter.rb +100 -0
  58. data/lib/docscribe/types/yard/parser.rb +240 -0
  59. data/lib/docscribe/types/yard/types.rb +52 -0
  60. data/lib/docscribe/version.rb +1 -1
  61. 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
- files = raw.dig('filter', 'files') || {}
15
- include_patterns = normalize_file_patterns(files['include'])
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
- # @private
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
- # @private
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
- # @private
97
- # @param [Array<String>] patterns
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
- # @private
112
- # @param [String] pattern
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') || DEFAULT.dig('filter', 'visibilities')).map(&:to_s)
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') || DEFAULT.dig('filter', 'exclude')).map(&:to_s).reject(&:empty?)
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') || DEFAULT.dig('filter', 'include')).map(&:to_s).reject(&:empty?)
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, nil] path optional config path
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
- YAML.safe_load_file(path, permitted_classes: [], permitted_symbols: [], aliases: true) || {}
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, nil] filename optional filename for diagnostics
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
- Psych.safe_load(
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'
@@ -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 ||= begin
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
- # Method documentation.
26
+ # Core rbs provider
36
27
  #
37
- # @raise [LoadError]
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 ||= begin
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
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
- # Method documentation.
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 [Object]
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
- if sorbet_enabled?
22
- begin
23
- require 'docscribe/types/sorbet/source_provider'
24
- providers << Docscribe::Types::Sorbet::SourceProvider.new(
25
- source: source,
26
- file: file,
27
- collapse_generics: sorbet_collapse_generics?
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
- providers << sorbet_rbi_provider
34
- end
34
+ providers << sorbet_source_provider(source, file)
35
+ providers << sorbet_rbi_provider
36
+ end
35
37
 
36
- providers << rbs_provider if rbs_enabled?
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
 
@@ -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, nil] raw user-provided config 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: when included, also defines #walk (instance visibility: private)
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