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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +149 -0
  3. data/lib/docscribe/cli/config_builder.rb +125 -35
  4. data/lib/docscribe/cli/generate.rb +288 -117
  5. data/lib/docscribe/cli/init.rb +49 -13
  6. data/lib/docscribe/cli/options.rb +302 -127
  7. data/lib/docscribe/cli/run.rb +391 -135
  8. data/lib/docscribe/cli.rb +23 -5
  9. data/lib/docscribe/config/defaults.rb +11 -11
  10. data/lib/docscribe/config/emit.rb +1 -0
  11. data/lib/docscribe/config/filtering.rb +24 -11
  12. data/lib/docscribe/config/loader.rb +7 -4
  13. data/lib/docscribe/config/plugin.rb +1 -0
  14. data/lib/docscribe/config/rbs.rb +31 -22
  15. data/lib/docscribe/config/sorbet.rb +41 -15
  16. data/lib/docscribe/config/sorting.rb +1 -0
  17. data/lib/docscribe/config/template.rb +1 -0
  18. data/lib/docscribe/config/utils.rb +1 -0
  19. data/lib/docscribe/config.rb +1 -0
  20. data/lib/docscribe/infer/constants.rb +15 -0
  21. data/lib/docscribe/infer/literals.rb +43 -25
  22. data/lib/docscribe/infer/names.rb +24 -15
  23. data/lib/docscribe/infer/params.rb +52 -6
  24. data/lib/docscribe/infer/raises.rb +24 -14
  25. data/lib/docscribe/infer/returns.rb +365 -182
  26. data/lib/docscribe/infer.rb +10 -9
  27. data/lib/docscribe/inline_rewriter/collector.rb +766 -375
  28. data/lib/docscribe/inline_rewriter/doc_block.rb +217 -74
  29. data/lib/docscribe/inline_rewriter/doc_builder.rb +1488 -602
  30. data/lib/docscribe/inline_rewriter/source_helpers.rb +100 -52
  31. data/lib/docscribe/inline_rewriter/tag_sorter.rb +109 -48
  32. data/lib/docscribe/inline_rewriter.rb +1009 -595
  33. data/lib/docscribe/plugin/base/collector_plugin.rb +2 -3
  34. data/lib/docscribe/plugin/base/tag_plugin.rb +1 -1
  35. data/lib/docscribe/plugin/registry.rb +34 -7
  36. data/lib/docscribe/plugin.rb +48 -17
  37. data/lib/docscribe/types/rbs/collection_loader.rb +0 -1
  38. data/lib/docscribe/types/rbs/provider.rb +75 -26
  39. data/lib/docscribe/types/rbs/type_formatter.rb +127 -59
  40. data/lib/docscribe/types/sorbet/base_provider.rb +31 -12
  41. data/lib/docscribe/version.rb +1 -1
  42. metadata +2 -2
@@ -4,6 +4,7 @@ require 'optparse'
4
4
 
5
5
  module Docscribe
6
6
  module CLI
7
+ # CLI option parsing and defaults.
7
8
  module Options
8
9
  DEFAULT = {
9
10
  stdin: false,
@@ -12,19 +13,58 @@ module Docscribe
12
13
  verbose: false,
13
14
  explain: false,
14
15
  config: nil,
15
- include: [],
16
- exclude: [],
17
- include_file: [],
18
- exclude_file: [],
16
+ include: [], #: Array[String]
17
+ exclude: [], #: Array[String]
18
+ include_file: [], #: Array[String]
19
+ exclude_file: [], #: Array[String]
19
20
  rbs: false,
20
- sig_dirs: [],
21
+ sig_dirs: [], #: Array[String]
21
22
  sorbet: false,
22
- rbi_dirs: [],
23
+ rbi_dirs: [], #: Array[String]
23
24
  rbs_collection: false
24
25
  }.freeze
25
26
 
26
27
  module_function
27
28
 
29
+ BANNER = <<~TEXT
30
+ Usage: docscribe [options] [files...]
31
+
32
+ Default behavior:
33
+ Inspect files and report what safe doc updates would be applied.
34
+
35
+ Autocorrect:
36
+ -a, --autocorrect Apply safe doc updates in place
37
+ (insert missing docs, merge existing doc-like blocks,
38
+ normalize tag order)
39
+ -A, --autocorrect-all Apply aggressive doc updates in place
40
+ (rebuild existing doc blocks)
41
+
42
+ Input / config:
43
+ --stdin Read code from STDIN and print rewritten output
44
+ -C, --config PATH Path to config YAML (default: docscribe.yml)
45
+
46
+ Type information:
47
+ --rbs Use RBS signatures for @param/@return when available
48
+ --sig-dir DIR Add an RBS signature directory (repeatable). Implies `--rbs`.
49
+ --sorbet Use Sorbet signatures from inline sigs / RBI files when available
50
+ --rbi-dir DIR Add a Sorbet RBI directory (repeatable). Implies --sorbet.
51
+ --rbs-collection Auto-discover RBS collection from rbs_collection.lock.yaml. Implies --rbs.
52
+
53
+ Filtering:
54
+ --include PATTERN Include PATTERN (method id or file path; glob or /regex/)
55
+ --exclude PATTERN Exclude PATTERN (method id or file path; glob or /regex/)
56
+ --include-file PATTERN Only process files matching PATTERN (glob or /regex/)
57
+ --exclude-file PATTERN Skip files matching PATTERN (glob or /regex/)
58
+
59
+ Output:
60
+ --verbose Print per-file actions
61
+ -e, --explain Show detailed reasons for changes
62
+
63
+ Other:
64
+ -v, --version Print version and exit
65
+ -h, --help Show this help
66
+ TEXT
67
+
28
68
  # Parse CLI arguments into normalized Docscribe runtime options.
29
69
  #
30
70
  # CLI behavior model:
@@ -41,126 +81,263 @@ module Docscribe
41
81
  # @return [Hash] normalized runtime options
42
82
  def parse!(argv)
43
83
  options = Marshal.load(Marshal.dump(DEFAULT))
44
- autocorrect_mode = nil
45
-
46
- parser = OptionParser.new do |opts|
47
- opts.banner = <<~TEXT
48
- Usage: docscribe [options] [files...]
49
-
50
- Default behavior:
51
- Inspect files and report what safe doc updates would be applied.
52
-
53
- Autocorrect:
54
- -a, --autocorrect Apply safe doc updates in place
55
- (insert missing docs, merge existing doc-like blocks,
56
- normalize tag order)
57
- -A, --autocorrect-all Apply aggressive doc updates in place
58
- (rebuild existing doc blocks)
59
-
60
- Input / config:
61
- --stdin Read code from STDIN and print rewritten output
62
- -C, --config PATH Path to config YAML (default: docscribe.yml)
63
-
64
- Type information:
65
- --rbs Use RBS signatures for @param/@return when available
66
- --sig-dir DIR Add an RBS signature directory (repeatable). Implies `--rbs`.
67
- --sorbet Use Sorbet signatures from inline sigs / RBI files when available
68
- --rbi-dir DIR Add a Sorbet RBI directory (repeatable). Implies --sorbet.
69
- --rbs-collection Auto-discover RBS collection from rbs_collection.lock.yaml. Implies --rbs.
70
-
71
- Filtering:
72
- --include PATTERN Include PATTERN (method id or file path; glob or /regex/)
73
- --exclude PATTERN Exclude PATTERN (method id or file path; glob or /regex/)
74
- --include-file PATTERN Only process files matching PATTERN (glob or /regex/)
75
- --exclude-file PATTERN Skip files matching PATTERN (glob or /regex/)
76
-
77
- Output:
78
- --verbose Print per-file actions
79
- -e, --explain Show detailed reasons for changes
80
-
81
- Other:
82
- -v, --version Print version and exit
83
- -h, --help Show this help
84
- TEXT
85
-
86
- opts.on('-a', '--autocorrect', 'Apply safe doc updates in place') do
87
- autocorrect_mode = :safe
88
- end
89
-
90
- opts.on('-A', '--autocorrect-all', 'Apply aggressive doc updates in place') do
91
- autocorrect_mode = :aggressive
92
- end
93
-
94
- opts.on('--stdin', 'Read code from STDIN and print rewritten output') do
95
- options[:stdin] = true
96
- end
97
-
98
- opts.on('-C', '--config PATH', 'Path to config YAML (default: docscribe.yml)') do |v|
99
- options[:config] = v
100
- end
101
-
102
- opts.on('--rbs', 'Use RBS signatures for @param/@return when available (falls back to inference)') do
103
- options[:rbs] = true
104
- end
105
-
106
- opts.on('--sig-dir DIR', 'Add an RBS signature directory (repeatable). Implies --rbs.') do |v|
107
- options[:rbs] = true
108
- options[:sig_dirs] << v
109
- end
110
-
111
- opts.on('--sorbet', 'Use Sorbet signatures from inline sigs / RBI files when available') do
112
- options[:sorbet] = true
113
- end
114
-
115
- opts.on('--rbi-dir DIR', 'Add a Sorbet RBI directory (repeatable). Implies --sorbet.') do |v|
116
- options[:sorbet] = true
117
- options[:rbi_dirs] << v
118
- end
119
-
120
- opts.on('--rbs-collection', 'Auto-discover RBS collection from rbs_collection.lock.yaml. Implies --rbs.') do
121
- options[:rbs] = true
122
- options[:rbs_collection] = true
123
- end
124
-
125
- opts.on('--include PATTERN', 'Include PATTERN (method id or file path; glob or /regex/)') do |v|
126
- route_include_exclude(options, :include, v)
127
- end
128
-
129
- opts.on('--exclude PATTERN',
130
- 'Exclude PATTERN (method id or file path; glob or /regex/). Exclude wins.') do |v|
131
- route_include_exclude(options, :exclude, v)
132
- end
133
-
134
- opts.on('--include-file PATTERN', 'Only process files matching PATTERN (glob or /regex/)') do |v|
135
- options[:include_file] << v
136
- end
137
-
138
- opts.on('--exclude-file PATTERN', 'Skip files matching PATTERN (glob or /regex/). Exclude wins.') do |v|
139
- options[:exclude_file] << v
140
- end
141
-
142
- opts.on('--verbose', 'Print per-file actions') do
143
- options[:verbose] = true
144
- end
145
-
146
- opts.on('-e', '--explain', 'Show detailed reasons for changes') do
147
- options[:explain] = true
148
- end
149
-
150
- opts.on('-v', '--version', 'Print version and exit') do
151
- require 'docscribe/version'
152
- puts Docscribe::VERSION
153
- exit 0
154
- end
155
-
156
- opts.on('-h', '--help', 'Show this help') do
157
- puts opts
158
- exit 0
159
- end
160
- end
161
-
162
- parser.parse!(argv)
84
+ autocorrect = { mode: nil }
85
+
86
+ build_option_parser(options, autocorrect).parse!(argv)
87
+ resolve_mode_and_strategy!(options, autocorrect[:mode])
88
+ options
89
+ end
90
+
91
+ # Build the OptionParser instance and register all CLI option groups.
92
+ #
93
+ # @note module_function: when included, also defines #build_option_parser (instance visibility: private)
94
+ # @param [Hash] options mutable parsed options hash
95
+ # @param [Hash{Symbol => Symbol,nil}] autocorrect mutable container for autocorrect mode
96
+ # @return [OptionParser]
97
+ def build_option_parser(options, autocorrect)
98
+ OptionParser.new do |opts|
99
+ opts.banner = BANNER
100
+ define_autocorrect_options(opts, autocorrect)
101
+ define_input_options(opts, options)
102
+ define_type_options(opts, options)
103
+ define_filter_options(opts, options)
104
+ define_output_options(opts, options)
105
+ define_misc_options(opts)
106
+ end
107
+ end
108
+
109
+ # @note module_function: when included, also defines # (instance visibility: private)
110
+ # @private
111
+ # @param [OptionParser] opts
112
+ # @param [Hash{Symbol => Symbol,nil}] autocorrect mutable container for autocorrect mode (:safe, :aggressive, nil)
113
+ # @return [void]
114
+ def define_autocorrect_options(opts, autocorrect)
115
+ opts.on('-a', '--autocorrect', 'Apply safe doc updates in place') do
116
+ autocorrect[:mode] = :safe
117
+ end
118
+
119
+ opts.on('-A', '--autocorrect-all', 'Apply aggressive doc updates in place') do
120
+ autocorrect[:mode] = :aggressive
121
+ end
122
+ end
123
+
124
+ # @note module_function: when included, also defines # (instance visibility: private)
125
+ # @private
126
+ # @param [OptionParser] opts
127
+ # @param [Hash] options mutable parsed options hash
128
+ # @return [void]
129
+ def define_input_options(opts, options)
130
+ define_stdin_option(opts, options)
131
+ define_config_option(opts, options)
132
+ end
133
+
134
+ # @note module_function: when included, also defines # (instance visibility: private)
135
+ # @private
136
+ # @param [OptionParser] opts
137
+ # @param [Hash] options mutable parsed options hash
138
+ # @return [void]
139
+ def define_stdin_option(opts, options)
140
+ opts.on('--stdin', 'Read code from STDIN and print rewritten output') do
141
+ options[:stdin] = true
142
+ end
143
+ end
144
+
145
+ # @note module_function: when included, also defines # (instance visibility: private)
146
+ # @private
147
+ # @param [OptionParser] opts
148
+ # @param [Hash] options mutable parsed options hash
149
+ # @return [void]
150
+ def define_config_option(opts, options)
151
+ opts.on('-C', '--config PATH', 'Path to config YAML (default: docscribe.yml)') do |v|
152
+ options[:config] = v
153
+ end
154
+ end
155
+
156
+ # @note module_function: when included, also defines # (instance visibility: private)
157
+ # @private
158
+ # @param [OptionParser] opts
159
+ # @param [Hash] options mutable parsed options hash
160
+ # @return [void]
161
+ def define_type_options(opts, options)
162
+ define_rbs_option(opts, options)
163
+ define_sig_dir_option(opts, options)
164
+ define_sorbet_option(opts, options)
165
+ define_rbi_dir_option(opts, options)
166
+ define_rbs_collection_option(opts, options)
167
+ end
168
+
169
+ # @note module_function: when included, also defines # (instance visibility: private)
170
+ # @private
171
+ # @param [OptionParser] opts
172
+ # @param [Hash] options
173
+ # @return [void]
174
+ def define_rbs_option(opts, options)
175
+ opts.on('--rbs', 'Use RBS signatures for @param/@return when available (falls back to inference)') do
176
+ options[:rbs] = true
177
+ end
178
+ end
179
+
180
+ # @note module_function: when included, also defines # (instance visibility: private)
181
+ # @private
182
+ # @param [OptionParser] opts
183
+ # @param [Hash] options
184
+ # @return [void]
185
+ def define_sig_dir_option(opts, options)
186
+ opts.on('--sig-dir DIR', 'Add an RBS signature directory (repeatable). Implies --rbs.') do |v|
187
+ options[:rbs] = true
188
+ options[:sig_dirs] << v
189
+ end
190
+ end
191
+
192
+ # @note module_function: when included, also defines # (instance visibility: private)
193
+ # @private
194
+ # @param [OptionParser] opts
195
+ # @param [Hash] options
196
+ # @return [void]
197
+ def define_sorbet_option(opts, options)
198
+ opts.on('--sorbet', 'Use Sorbet signatures from inline sigs / RBI files when available') do
199
+ options[:sorbet] = true
200
+ end
201
+ end
202
+
203
+ # @note module_function: when included, also defines # (instance visibility: private)
204
+ # @private
205
+ # @param [OptionParser] opts
206
+ # @param [Hash] options
207
+ # @return [void]
208
+ def define_rbi_dir_option(opts, options)
209
+ opts.on('--rbi-dir DIR', 'Add a Sorbet RBI directory (repeatable). Implies --sorbet.') do |v|
210
+ options[:sorbet] = true
211
+ options[:rbi_dirs] << v
212
+ end
213
+ end
214
+
215
+ # @note module_function: when included, also defines # (instance visibility: private)
216
+ # @private
217
+ # @param [OptionParser] opts
218
+ # @param [Hash] options
219
+ # @return [void]
220
+ def define_rbs_collection_option(opts, options)
221
+ opts.on('--rbs-collection', 'Auto-discover RBS collection from rbs_collection.lock.yaml. Implies --rbs.') do
222
+ options[:rbs] = true
223
+ options[:rbs_collection] = true
224
+ end
225
+ end
226
+
227
+ # @note module_function: when included, also defines # (instance visibility: private)
228
+ # @private
229
+ # @param [OptionParser] opts
230
+ # @param [Hash] options mutable parsed options hash
231
+ # @return [void]
232
+ def define_filter_options(opts, options)
233
+ define_include_option(opts, options)
234
+ define_exclude_option(opts, options)
235
+ define_include_file_option(opts, options)
236
+ define_exclude_file_option(opts, options)
237
+ end
238
+
239
+ # @note module_function: when included, also defines # (instance visibility: private)
240
+ # @private
241
+ # @param [OptionParser] opts
242
+ # @param [Hash] options mutable parsed options hash
243
+ # @return [void]
244
+ def define_include_option(opts, options)
245
+ opts.on('--include PATTERN', 'Include PATTERN (method id or file path; glob or /regex/)') do |v|
246
+ route_include_exclude(options, :include, v)
247
+ end
248
+ end
249
+
250
+ # @note module_function: when included, also defines # (instance visibility: private)
251
+ # @private
252
+ # @param [OptionParser] opts
253
+ # @param [Hash] options mutable parsed options hash
254
+ # @return [void]
255
+ def define_exclude_option(opts, options)
256
+ opts.on('--exclude PATTERN',
257
+ 'Exclude PATTERN (method id or file path; glob or /regex/). Exclude wins.') do |v|
258
+ route_include_exclude(options, :exclude, v)
259
+ end
260
+ end
261
+
262
+ # @note module_function: when included, also defines # (instance visibility: private)
263
+ # @private
264
+ # @param [OptionParser] opts
265
+ # @param [Hash] options mutable parsed options hash
266
+ # @return [void]
267
+ def define_include_file_option(opts, options)
268
+ opts.on('--include-file PATTERN', 'Only process files matching PATTERN (glob or /regex/)') do |v|
269
+ options[:include_file] << v
270
+ end
271
+ end
272
+
273
+ # @note module_function: when included, also defines # (instance visibility: private)
274
+ # @private
275
+ # @param [OptionParser] opts
276
+ # @param [Hash] options mutable parsed options hash
277
+ # @return [void]
278
+ def define_exclude_file_option(opts, options)
279
+ opts.on('--exclude-file PATTERN', 'Skip files matching PATTERN (glob or /regex/). Exclude wins.') do |v|
280
+ options[:exclude_file] << v
281
+ end
282
+ end
163
283
 
284
+ # @note module_function: when included, also defines # (instance visibility: private)
285
+ # @private
286
+ # @param [OptionParser] opts
287
+ # @param [Hash] options mutable parsed options hash
288
+ # @return [void]
289
+ def define_output_options(opts, options)
290
+ define_verbose_option(opts, options)
291
+ define_explain_option(opts, options)
292
+ end
293
+
294
+ # @note module_function: when included, also defines # (instance visibility: private)
295
+ # @private
296
+ # @param [OptionParser] opts
297
+ # @param [Hash] options mutable parsed options hash
298
+ # @return [void]
299
+ def define_verbose_option(opts, options)
300
+ opts.on('--verbose', 'Print per-file actions') do
301
+ options[:verbose] = true
302
+ end
303
+ end
304
+
305
+ # @note module_function: when included, also defines # (instance visibility: private)
306
+ # @private
307
+ # @param [OptionParser] opts
308
+ # @param [Hash] options mutable parsed options hash
309
+ # @return [void]
310
+ def define_explain_option(opts, options)
311
+ opts.on('-e', '--explain', 'Show detailed reasons for changes') do
312
+ options[:explain] = true
313
+ end
314
+ end
315
+
316
+ # @note module_function: when included, also defines # (instance visibility: private)
317
+ # @private
318
+ # @param [OptionParser] opts
319
+ # @return [void]
320
+ def define_misc_options(opts)
321
+ opts.on('-v', '--version', 'Print version and exit') do
322
+ require 'docscribe/version'
323
+ puts Docscribe::VERSION
324
+ exit 0
325
+ end
326
+
327
+ opts.on('-h', '--help', 'Show this help') do
328
+ puts opts
329
+ exit 0
330
+ end
331
+ end
332
+
333
+ # Set the runtime mode and strategy after all options have been parsed.
334
+ #
335
+ # @note module_function: when included, also defines # (instance visibility: private)
336
+ # @private
337
+ # @param [Hash] options mutable parsed options hash
338
+ # @param [Symbol, nil] autocorrect_mode autocorrect mode selected (:safe, :aggressive, or nil)
339
+ # @return [void]
340
+ def resolve_mode_and_strategy!(options, autocorrect_mode)
164
341
  if options[:stdin]
165
342
  options[:mode] = :stdin
166
343
  options[:strategy] = autocorrect_mode || :safe
@@ -171,8 +348,6 @@ module Docscribe
171
348
  options[:mode] = :check
172
349
  options[:strategy] = :safe
173
350
  end
174
-
175
- options
176
351
  end
177
352
 
178
353
  # Route an include/exclude pattern into method filters or file filters.