docscribe 1.4.2 → 1.5.1

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +601 -139
  3. data/exe/docscribe-client +105 -0
  4. data/lib/docscribe/cli/check_for_comments.rb +183 -0
  5. data/lib/docscribe/cli/config_builder.rb +107 -53
  6. data/lib/docscribe/cli/formatters/json.rb +294 -0
  7. data/lib/docscribe/cli/formatters/sarif.rb +235 -0
  8. data/lib/docscribe/cli/formatters/text.rb +208 -0
  9. data/lib/docscribe/cli/formatters.rb +26 -0
  10. data/lib/docscribe/cli/generate.rb +56 -62
  11. data/lib/docscribe/cli/init.rb +14 -6
  12. data/lib/docscribe/cli/options.rb +206 -89
  13. data/lib/docscribe/cli/rbs_gen.rb +529 -0
  14. data/lib/docscribe/cli/run.rb +433 -154
  15. data/lib/docscribe/cli/server.rb +135 -0
  16. data/lib/docscribe/cli/sigs.rb +366 -0
  17. data/lib/docscribe/cli/update_types.rb +103 -0
  18. data/lib/docscribe/cli.rb +21 -24
  19. data/lib/docscribe/config/defaults.rb +7 -2
  20. data/lib/docscribe/config/emit.rb +17 -0
  21. data/lib/docscribe/config/filtering.rb +17 -24
  22. data/lib/docscribe/config/loader.rb +19 -17
  23. data/lib/docscribe/config/plugin.rb +1 -1
  24. data/lib/docscribe/config/rbs.rb +39 -7
  25. data/lib/docscribe/config/sorbet.rb +22 -16
  26. data/lib/docscribe/config/sorting.rb +1 -1
  27. data/lib/docscribe/config/template.rb +10 -1
  28. data/lib/docscribe/config/utils.rb +11 -9
  29. data/lib/docscribe/config.rb +10 -6
  30. data/lib/docscribe/infer/ast_walk.rb +1 -1
  31. data/lib/docscribe/infer/literals.rb +6 -11
  32. data/lib/docscribe/infer/names.rb +2 -3
  33. data/lib/docscribe/infer/params.rb +14 -16
  34. data/lib/docscribe/infer/raises.rb +3 -5
  35. data/lib/docscribe/infer/returns.rb +615 -151
  36. data/lib/docscribe/infer.rb +29 -26
  37. data/lib/docscribe/inline_rewriter/collector.rb +159 -164
  38. data/lib/docscribe/inline_rewriter/doc_block.rb +145 -115
  39. data/lib/docscribe/inline_rewriter/doc_builder.rb +1032 -723
  40. data/lib/docscribe/inline_rewriter/source_helpers.rb +48 -48
  41. data/lib/docscribe/inline_rewriter/tag_sorter.rb +82 -85
  42. data/lib/docscribe/inline_rewriter.rb +485 -488
  43. data/lib/docscribe/lru_cache.rb +49 -0
  44. data/lib/docscribe/parsing.rb +28 -9
  45. data/lib/docscribe/plugin/base/collector_plugin.rb +2 -1
  46. data/lib/docscribe/plugin/base/tag_plugin.rb +0 -1
  47. data/lib/docscribe/plugin/context.rb +28 -18
  48. data/lib/docscribe/plugin/registry.rb +25 -26
  49. data/lib/docscribe/plugin/tag.rb +9 -14
  50. data/lib/docscribe/plugin.rb +17 -16
  51. data/lib/docscribe/server.rb +608 -0
  52. data/lib/docscribe/types/provider_chain.rb +4 -2
  53. data/lib/docscribe/types/rbs/collection_loader.rb +2 -2
  54. data/lib/docscribe/types/rbs/provider.rb +177 -51
  55. data/lib/docscribe/types/rbs/type_formatter.rb +224 -83
  56. data/lib/docscribe/types/signature.rb +22 -42
  57. data/lib/docscribe/types/sorbet/base_provider.rb +29 -21
  58. data/lib/docscribe/types/sorbet/rbi_provider.rb +6 -5
  59. data/lib/docscribe/types/sorbet/source_provider.rb +6 -4
  60. data/lib/docscribe/types/yard/formatter.rb +100 -0
  61. data/lib/docscribe/types/yard/parser.rb +240 -0
  62. data/lib/docscribe/types/yard/types.rb +52 -0
  63. data/lib/docscribe/version.rb +1 -1
  64. metadata +38 -1
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+
5
+ module Docscribe
6
+ module CLI
7
+ # Handle the `docscribe server` subcommand.
8
+ module ServerCmd
9
+ BANNER = <<~TEXT
10
+ Usage: docscribe server <command> [options]
11
+
12
+ Commands:
13
+ start Start the background daemon
14
+ stop Stop the background daemon
15
+ status Show daemon status
16
+
17
+ Options:
18
+ -C, --config <path> Config file path (default: auto-detect)
19
+
20
+ Once the server is running, use `--server` with other commands:
21
+ docscribe --server check lib/
22
+ docscribe --server --autocorrect lib/
23
+ TEXT
24
+
25
+ class << self
26
+ # Run the server subcommand.
27
+ #
28
+ # @param [Array<String>] argv subcommand arguments
29
+ # @return [Integer] exit code
30
+ def run(argv)
31
+ config_path, cmd = parse_args(argv)
32
+ return warn(BANNER) || 1 unless cmd
33
+
34
+ case cmd
35
+ when 'start' then start_server(config_path)
36
+ when 'stop' then stop_server(config_path)
37
+ when 'status' then show_status(config_path)
38
+ else warn(usage) || 1
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ # @private
45
+ # @param [Array<String>] argv
46
+ # @return [(String?, String?)]
47
+ def parse_args(argv)
48
+ config_path = nil
49
+ rest = OptionParser.new do |opts|
50
+ opts.on('-C', '--config <path>', 'Config file path') { |v| config_path = v }
51
+ end.parse!(argv.dup)
52
+ [config_path, rest.first]
53
+ end
54
+
55
+ # Start the background daemon process.
56
+ #
57
+ # @private
58
+ # @param [String?] config_path optional config file path
59
+ # @return [Integer] exit code
60
+ def start_server(config_path = nil)
61
+ require 'docscribe/server'
62
+ return already_running(config_path) if Docscribe::Server.running?(config_path)
63
+
64
+ Docscribe::Server.ensure_running!(daemonize: true, config_path: config_path)
65
+ pid = Docscribe::Server.read_pid(config_path)
66
+ warn "Docscribe server started (pid #{pid})"
67
+ 0
68
+ end
69
+
70
+ # @private
71
+ # @param [String?] config_path optional config file path
72
+ # @return [Integer]
73
+ def already_running(config_path = nil)
74
+ pid = Docscribe::Server.read_pid(config_path)
75
+ warn "Docscribe server already running (pid #{pid})"
76
+ 0
77
+ end
78
+
79
+ # Stop the background daemon.
80
+ #
81
+ # @private
82
+ # @param [String?] config_path optional config file path
83
+ # @return [Integer] exit code
84
+ def stop_server(config_path = nil)
85
+ require 'docscribe/server'
86
+ alive = Docscribe::Server::Client.new(config_path: config_path).shutdown
87
+ warn(alive ? 'Docscribe server stopped' : 'Docscribe server is not running')
88
+ 0
89
+ end
90
+
91
+ # Show the server status.
92
+ #
93
+ # @private
94
+ # @param [String?] config_path optional config file path
95
+ # @return [Integer] exit code
96
+ def show_status(config_path = nil)
97
+ require 'docscribe/server'
98
+ return warn('Docscribe server is not running') || 0 unless Docscribe::Server.running?(config_path)
99
+
100
+ info = Docscribe::Server::Client.new(config_path: config_path).ping
101
+ info ? show_status_from_ping(info) : show_status_fallback(config_path)
102
+ 0
103
+ end
104
+
105
+ # @private
106
+ # @param [Hash<String, Object>] info ping response hash
107
+ # @return [void]
108
+ def show_status_from_ping(info)
109
+ pid = info.dig('result', 'pid')
110
+ version = info.dig('result', 'version')
111
+ uptime = info.dig('result', 'uptime')
112
+ sock = info.dig('result', 'socket_path')
113
+ warn "Docscribe server v#{version} is running (pid #{pid}, socket #{sock}, uptime #{uptime}s)"
114
+ end
115
+
116
+ # @private
117
+ # @param [String?] config_path
118
+ # @return [void]
119
+ def show_status_fallback(config_path)
120
+ pid = Docscribe::Server.read_pid(config_path)
121
+ sock = Docscribe::Server.socket_path(config_path)
122
+ warn "Docscribe server is running (pid #{pid}, socket #{sock})"
123
+ end
124
+
125
+ # Print usage information for the server subcommand.
126
+ #
127
+ # @private
128
+ # @return [String]
129
+ def usage
130
+ BANNER
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,366 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'docscribe/parsing'
5
+ require 'docscribe/types/rbs/provider'
6
+
7
+ module Docscribe
8
+ module CLI
9
+ # Check RBS signature coverage for Ruby source files.
10
+ #
11
+ # Usage:
12
+ # docscribe sigs [options] [files...]
13
+ #
14
+ # Parses Ruby source files, extracts method definitions, and checks
15
+ # each method against the configured RBS signature directories.
16
+ # Reports methods that lack RBS type signatures.
17
+ module Sigs
18
+ BANNER = <<~TEXT
19
+ Usage: docscribe sigs [options] [files...]
20
+
21
+ Check RBS signature coverage for Ruby source files.
22
+
23
+ TEXT
24
+
25
+ EXIT_CODES = "\nExit codes:\n " \
26
+ "0 - all methods have signatures\n " \
27
+ "1 - some methods lack signatures\n " \
28
+ '2 - error occurred'
29
+
30
+ # @!attribute [rw] name
31
+ # @return [Symbol]
32
+ # @param [Symbol] value
33
+ #
34
+ # @!attribute [rw] scope
35
+ # @return [Symbol]
36
+ # @param [Symbol] value
37
+ #
38
+ # @!attribute [rw] container
39
+ # @return [String?]
40
+ # @param [String?] value
41
+ #
42
+ # @!attribute [rw] file
43
+ # @return [String]
44
+ # @param [String] value
45
+ #
46
+ # @!attribute [rw] line
47
+ # @return [Integer]
48
+ # @param [Integer] value
49
+ MethodDef = Struct.new(:name, :scope, :container, :file, :line, keyword_init: true)
50
+
51
+ class << self
52
+ # @param [Array<String>] argv
53
+ # @return [Integer]
54
+ def run(argv)
55
+ warn_ruby_version
56
+ options = parse_options(argv)
57
+ paths = expand_paths(argv)
58
+ return no_files_found if paths.empty?
59
+
60
+ run_with(options, extract_methods(paths))
61
+ end
62
+
63
+ private
64
+
65
+ # @private
66
+ # @return [void]
67
+ def warn_ruby_version
68
+ return unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0')
69
+
70
+ warn 'Warning: docscribe sigs requires Ruby 3.0+ for RBS support. ' \
71
+ "You are running Ruby #{RUBY_VERSION}."
72
+ end
73
+
74
+ # @private
75
+ # @param [Array<String>] argv
76
+ # @return [Hash<Symbol, Object>]
77
+ def parse_options(argv)
78
+ options = { sig_dirs: ['sig'], rbs_collection: false, verbose: false }
79
+
80
+ parser = OptionParser.new do |opts|
81
+ opts.banner = BANNER
82
+ register_sig_options(opts, options)
83
+ end
84
+
85
+ parser.parse!(argv)
86
+ options
87
+ end
88
+
89
+ # @private
90
+ # @param [OptionParser] opts
91
+ # @param [Hash<Symbol, Object>] options
92
+ # @return [void]
93
+ def register_sig_options(opts, options)
94
+ opts.on('-s', '--sig-dir DIR', 'Add RBS signature directory (repeatable)') { |d| options[:sig_dirs] << d }
95
+ opts.on('--rbs-collection', 'Use RBS collection') { options[:rbs_collection] = true }
96
+ opts.on('--verbose', 'Print methods that have signatures too') { options[:verbose] = true }
97
+ opts.on('-h', '--help', 'Show this help') do
98
+ puts opts, EXIT_CODES
99
+ exit 0
100
+ end
101
+ end
102
+
103
+ # @private
104
+ # @param [Array<String>] args
105
+ # @return [Array<String>]
106
+ def expand_paths(args)
107
+ files = [] #: Array[String]
108
+ args = ['.'] if args.empty?
109
+ args.each { |path| expand_single_path(files, path) }
110
+ files.uniq.sort
111
+ end
112
+
113
+ # @private
114
+ # @param [Array<String>] files
115
+ # @param [String] path
116
+ # @return [void]
117
+ def expand_single_path(files, path)
118
+ if File.directory?(path)
119
+ files.concat(Dir.glob(File.join(path, '**', '*.rb')))
120
+ elsif File.file?(path)
121
+ files << path
122
+ else
123
+ warn "Skipping missing path: #{path}"
124
+ end
125
+ end
126
+
127
+ # @private
128
+ # @return [Integer]
129
+ def no_files_found
130
+ warn 'No files found. Pass files or directories (e.g. `docscribe sigs lib`).'
131
+ 2
132
+ end
133
+
134
+ # @private
135
+ # @param [Hash<Symbol, Object>] options
136
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
137
+ # @return [Integer]
138
+ def run_with(options, methods)
139
+ return 0 if methods.empty?
140
+
141
+ provider = build_provider(options)
142
+ return 2 unless provider
143
+
144
+ missing = check_sigs(methods, provider, verbose: options[:verbose])
145
+ report_results(methods, missing)
146
+ missing.empty? ? 0 : 1
147
+ end
148
+
149
+ # @private
150
+ # @param [Array<String>] paths
151
+ # @return [Array<Docscribe::CLI::Sigs::MethodDef>]
152
+ def extract_methods(paths)
153
+ methods = [] #: Array[MethodDef]
154
+ paths.each { |path| extract_methods_from_file(path, methods) }
155
+ methods
156
+ end
157
+
158
+ # @private
159
+ # @param [String] path
160
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
161
+ # @raise [Parser::SyntaxError]
162
+ # @raise [StandardError]
163
+ # @return [void]
164
+ # @return [nil] if Parser::SyntaxError
165
+ # @return [nil] if StandardError
166
+ def extract_methods_from_file(path, methods)
167
+ src = File.read(path)
168
+ ast = Docscribe::Parsing.parse(src, file: path)
169
+ return unless ast
170
+
171
+ walk_for_methods(ast, [], methods, path)
172
+ rescue Parser::SyntaxError => e # steep:ignore
173
+ warn "Syntax error in #{path}: #{e.message}"
174
+ rescue StandardError => e
175
+ warn "Error parsing #{path}: #{e.class}: #{e.message}"
176
+ end
177
+
178
+ # @private
179
+ # @param [Parser::AST::Node] node
180
+ # @param [Array<String>] containers
181
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
182
+ # @param [String] path
183
+ # @param [Boolean] inside_sclass
184
+ # @return [void]
185
+ def walk_for_methods(node, containers, methods, path, inside_sclass: false)
186
+ return unless node.is_a?(Parser::AST::Node)
187
+
188
+ case node.type
189
+ when :class, :module then walk_class_module(node, containers, methods, path)
190
+ when :sclass then walk_sclass(node, containers, methods, path)
191
+ when :def then collect_def(node, containers, methods, path, inside_sclass: inside_sclass)
192
+ when :defs then collect_defs(node, containers, methods, path)
193
+ else walk_children(node, containers, methods, path, inside_sclass: inside_sclass)
194
+ end
195
+ end
196
+
197
+ # @private
198
+ # @param [Parser::AST::Node] node
199
+ # @param [Array<String>] containers
200
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
201
+ # @param [String] path
202
+ # @return [void]
203
+ def walk_class_module(node, containers, methods, path)
204
+ containers.push(const_name(node.children[0]))
205
+ node.children.drop(1).each { |c| walk_for_methods(c, containers, methods, path) }
206
+ containers.pop
207
+ end
208
+
209
+ # @private
210
+ # @param [Parser::AST::Node] node
211
+ # @param [Array<String>] containers
212
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
213
+ # @param [String] path
214
+ # @return [void]
215
+ def walk_sclass(node, containers, methods, path)
216
+ node.children.drop(1).each { |c| walk_for_methods(c, containers, methods, path, inside_sclass: true) }
217
+ end
218
+
219
+ # @private
220
+ # @param [Parser::AST::Node] node
221
+ # @param [Array<String>] containers
222
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
223
+ # @param [String] path
224
+ # @param [Boolean] inside_sclass
225
+ # @return [void]
226
+ def walk_children(node, containers, methods, path, inside_sclass: false)
227
+ node.children.each { |c| walk_for_methods(c, containers, methods, path, inside_sclass: inside_sclass) }
228
+ end
229
+
230
+ # @private
231
+ # @param [Parser::AST::Node] node
232
+ # @param [Array<String>] containers
233
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
234
+ # @param [String] path
235
+ # @param [Boolean] inside_sclass
236
+ # @return [void]
237
+ def collect_def(node, containers, methods, path, inside_sclass: false)
238
+ methods << MethodDef.new(
239
+ name: node.children[0],
240
+ scope: inside_sclass ? :class : :instance,
241
+ container: container_name(containers),
242
+ file: path,
243
+ line: node.loc&.line || 1
244
+ )
245
+ end
246
+
247
+ # @private
248
+ # @param [Parser::AST::Node] node
249
+ # @param [Array<String>] containers
250
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
251
+ # @param [String] path
252
+ # @return [void]
253
+ def collect_defs(node, containers, methods, path)
254
+ methods << MethodDef.new(
255
+ name: node.children[1],
256
+ scope: :class,
257
+ container: container_name(containers),
258
+ file: path,
259
+ line: node.loc&.line || 1
260
+ )
261
+ end
262
+
263
+ # @private
264
+ # @param [Array<String>] containers
265
+ # @return [String?]
266
+ def container_name(containers)
267
+ containers.empty? ? nil : containers.join('::')
268
+ end
269
+
270
+ # @private
271
+ # @param [Parser::AST::Node] node
272
+ # @return [String]
273
+ def const_name(node)
274
+ return node.to_s unless node.is_a?(Parser::AST::Node)
275
+ return node.children[1].to_s if node.type == :const
276
+
277
+ node.children.map { |c| c.is_a?(Parser::AST::Node) ? const_name(c) : c.to_s }.join('::')
278
+ end
279
+
280
+ # @private
281
+ # @param [Hash<Symbol, Object>] options
282
+ # @raise [LoadError]
283
+ # @raise [StandardError]
284
+ # @return [Docscribe::Types::RBS::Provider?]
285
+ # @return [nil] if LoadError
286
+ # @return [nil] if StandardError
287
+ def build_provider(options)
288
+ dirs = options[:rbs_collection] ? load_collection_dirs : [] #: Array[String]
289
+ Docscribe::Types::RBS::Provider.new(sig_dirs: options[:sig_dirs], collection_dirs: dirs)
290
+ rescue LoadError
291
+ warn 'Docscribe: rbs gem is not installed. Add `gem "rbs"` to your Gemfile ' \
292
+ 'or run `bundle exec rbs collection install`.'
293
+ nil
294
+ rescue StandardError => e
295
+ warn "Docscribe: Failed to initialize RBS provider: #{e.class}: #{e.message}"
296
+ nil
297
+ end
298
+
299
+ # @private
300
+ # @raise [StandardError]
301
+ # @return [Array<String>]
302
+ # @return [Array] if StandardError
303
+ def load_collection_dirs
304
+ dir = Docscribe::Types::RBS::CollectionLoader.resolve
305
+ dir ? [dir] : []
306
+ rescue StandardError => e
307
+ warn "Docscribe: Failed to load RBS collection: #{e.class}: #{e.message}"
308
+ []
309
+ end
310
+
311
+ # @private
312
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
313
+ # @param [Docscribe::Types::RBS::Provider] provider
314
+ # @param [Boolean] verbose
315
+ # @return [Array<Docscribe::CLI::Sigs::MethodDef>]
316
+ def check_sigs(methods, provider, verbose:)
317
+ missing = [] #: Array[MethodDef]
318
+ methods.each do |m|
319
+ sig = lookup_signature(m, provider)
320
+ puts " OK #{format_method(m)} (#{m.file}:#{m.line})" if sig && verbose
321
+ puts " MISS #{format_method(m)} (#{m.file}:#{m.line})" unless sig
322
+ missing << m unless sig
323
+ end
324
+ missing
325
+ end
326
+
327
+ # @private
328
+ # @param [Docscribe::CLI::Sigs::MethodDef] method_def
329
+ # @param [Docscribe::Types::RBS::Provider] provider
330
+ # @return [Object, nil]
331
+ def lookup_signature(method_def, provider)
332
+ container = method_def.container
333
+ return nil unless container
334
+
335
+ provider.signature_for(
336
+ container: container,
337
+ scope: method_def.scope,
338
+ name: method_def.name
339
+ )
340
+ end
341
+
342
+ # @private
343
+ # @param [Docscribe::CLI::Sigs::MethodDef] method_def
344
+ # @return [String]
345
+ def format_method(method_def)
346
+ prefix = method_def.scope == :class ? 'self.' : ''
347
+ container = method_def.container ? "#{method_def.container}#" : ''
348
+ "#{container}#{prefix}#{method_def.name}"
349
+ end
350
+
351
+ # @private
352
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
353
+ # @param [Array<Docscribe::CLI::Sigs::MethodDef>] missing
354
+ # @return [void]
355
+ def report_results(methods, missing)
356
+ puts
357
+ if missing.empty?
358
+ puts "Docscribe: All #{methods.size} methods have RBS signatures"
359
+ else
360
+ puts "Docscribe: #{missing.size}/#{methods.size} methods missing RBS signatures"
361
+ end
362
+ end
363
+ end
364
+ end
365
+ end
366
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+
5
+ require 'docscribe/cli/options'
6
+ require 'docscribe/cli/run'
7
+
8
+ module Docscribe
9
+ module CLI
10
+ # Two-pass update: rebuild docs then re-merge with RBS types.
11
+ #
12
+ # Usage:
13
+ # docscribe update_types [directory]
14
+ #
15
+ # Pass 1: `-AkB --rbs-collection <dir>` — aggressive rebuild, keep descriptions,
16
+ # no boilerplate, using RBS collection signatures.
17
+ # Pass 2: `-aB --rbs-collection <dir>` — safe merge cleanup, no boilerplate,
18
+ # using RBS collection signatures.
19
+ module UpdateTypes
20
+ BANNER = <<~TEXT
21
+ Usage: docscribe update_types [directory]
22
+
23
+ Two-pass type-aware documentation update.
24
+
25
+ Pass 1 (aggressive): docscribe -AkB --rbs-collection <dir>
26
+ rebuild doc blocks, keep descriptions, no boilerplate
27
+
28
+ Pass 2 (safe): docscribe -aB --rbs-collection <dir>
29
+ safe merge cleanup, no boilerplate
30
+
31
+ TEXT
32
+
33
+ class << self
34
+ # @param [Array<String>] argv
35
+ # @return [Integer]
36
+ def run(argv)
37
+ options = parse_options(argv)
38
+ dir = options[:dir]
39
+
40
+ announce_start
41
+
42
+ exit1 = run_first_pass(dir)
43
+ return exit1 unless exit1.zero?
44
+
45
+ exit2 = run_second_pass(dir)
46
+ return exit2 unless exit2.zero?
47
+
48
+ announce_complete
49
+ 0
50
+ end
51
+
52
+ private
53
+
54
+ # @private
55
+ # @param [Array<String>] argv
56
+ # @return [Hash<Symbol, Object>]
57
+ def parse_options(argv)
58
+ options = { dir: '.' }
59
+ OptionParser.new(BANNER) do |opts|
60
+ opts.on('-h', '--help', 'Show this help') { puts opts or exit 0 }
61
+ opts.parse!(argv)
62
+ end
63
+ options[:dir] = argv.first if argv.any?
64
+ options
65
+ end
66
+
67
+ # @private
68
+ # @return [void]
69
+ def announce_start
70
+ puts 'Docscribe: Running type-aware documentation update...'
71
+ puts
72
+ end
73
+
74
+ # @private
75
+ # @param [String] dir
76
+ # @return [Integer]
77
+ def run_first_pass(dir)
78
+ puts 'Pass 1: Aggressive rebuild with RBS collection...'
79
+ argv1 = ['-AkB', '--rbs-collection', dir]
80
+ options1 = Docscribe::CLI::Options.parse!(argv1)
81
+ Docscribe::CLI::Run.run(options: options1, argv: [dir])
82
+ end
83
+
84
+ # @private
85
+ # @param [String] dir
86
+ # @return [Integer]
87
+ def run_second_pass(dir)
88
+ puts 'Pass 2: Safe merge with RBS collection...'
89
+ argv2 = ['-aB', '--rbs-collection', dir]
90
+ options2 = Docscribe::CLI::Options.parse!(argv2)
91
+ Docscribe::CLI::Run.run(options: options2, argv: [dir])
92
+ end
93
+
94
+ # @private
95
+ # @return [void]
96
+ def announce_complete
97
+ puts
98
+ puts 'Docscribe: Type-aware documentation update complete.'
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
data/lib/docscribe/cli.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'docscribe/cli/init'
4
- require 'docscribe/cli/generate'
5
3
  require 'docscribe/cli/options'
6
4
  require 'docscribe/cli/run'
7
5
 
@@ -9,15 +7,8 @@ module Docscribe
9
7
  # CLI entry point and command dispatch.
10
8
  module CLI
11
9
  class << self
12
- # Main CLI entry point.
13
- #
14
- # Dispatches:
15
- # - `docscribe init ...` to the config-template generator
16
- # - `docscribe generate ...` to the plugin skeleton generator
17
- # - all other commands to the main option parser and runner
18
- #
19
- # @param [Array<String>] argv raw command-line arguments
20
- # @return [Integer] process exit code
10
+ # @param [Array<String>] argv
11
+ # @return [Integer]
21
12
  def run(argv)
22
13
  argv = argv.dup
23
14
  return dispatch_subcommand(argv) if subcommand?(argv.first)
@@ -26,29 +17,35 @@ module Docscribe
26
17
  Docscribe::CLI::Run.run(options: options, argv: argv)
27
18
  end
28
19
 
20
+ COMMANDS = {
21
+ 'init' => :Init,
22
+ 'generate' => :Generate,
23
+ 'sigs' => :Sigs,
24
+ 'rbs' => :RbsGen,
25
+ 'update_types' => :UpdateTypes,
26
+ 'check_for_comments' => :CheckForComments,
27
+ 'server' => :ServerCmd
28
+ }.freeze
29
+
29
30
  private
30
31
 
31
32
  # @private
32
- # @param [String] cmd
33
+ # @param [String?] cmd
33
34
  # @return [Boolean]
34
35
  def subcommand?(cmd)
35
- %w[init generate].include?(cmd)
36
+ COMMANDS.key?(cmd)
36
37
  end
37
38
 
38
39
  # @private
39
40
  # @param [Array<String>] argv
40
- # @return [Integer, nil]
41
+ # @return [Integer]
41
42
  def dispatch_subcommand(argv)
42
- case argv.first
43
- when 'init'
44
- argv.shift
45
- Docscribe::CLI::Init.run(argv)
46
- when 'generate'
47
- argv.shift
48
- Docscribe::CLI::Generate.run(argv)
49
- else
50
- 0
51
- end
43
+ cmd = argv.shift
44
+ const_name = COMMANDS[cmd]
45
+ return 0 unless const_name
46
+
47
+ require "docscribe/cli/#{cmd == 'rbs' ? 'rbs_gen' : cmd}"
48
+ Docscribe::CLI.const_get(const_name).run(argv)
52
49
  end
53
50
  end
54
51
  end
@@ -63,13 +63,18 @@ module Docscribe
63
63
  'collection' => false,
64
64
  'sig_dirs' => ['sig'],
65
65
  'collection_dirs' => [], #: Array[String]
66
- 'collapse_generics' => false
66
+ 'collapse_generics' => false,
67
+ 'warn_missing_collection' => true,
68
+ 'collapse_object_generics' => false
67
69
  },
68
70
  'sorbet' => {
69
71
  'enabled' => false,
70
72
  'rbi_dirs' => ['sorbet/rbi', 'rbi'],
71
- 'collapse_generics' => false
73
+ 'collapse_generics' => false,
74
+ 'collapse_object_generics' => false
72
75
  },
76
+ 'keep_descriptions' => false,
77
+ 'skip_anonymous_block_params' => false,
73
78
  'plugins' => {
74
79
  'require' => [] #: Array[String]
75
80
  }