docscribe 1.5.0 → 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.
@@ -4,7 +4,6 @@ require 'pathname'
4
4
 
5
5
  require 'docscribe/cli/config_builder'
6
6
  require 'docscribe/cli/formatters'
7
- require 'docscribe/inline_rewriter'
8
7
 
9
8
  module Docscribe
10
9
  module CLI
@@ -35,6 +34,14 @@ module Docscribe
35
34
  total: 0,
36
35
  processed: 0
37
36
  }.freeze
37
+
38
+ CLI_OVERRIDE_KEYS = %i[
39
+ keep_descriptions no_boilerplate
40
+ include exclude include_file exclude_file
41
+ rbs rbs_collection sig_dirs
42
+ sorbet rbi_dirs
43
+ ].freeze
44
+
38
45
  # Run Docscribe for files or STDIN using the selected mode and strategy.
39
46
  #
40
47
  # Modes:
@@ -50,6 +57,9 @@ module Docscribe
50
57
  # @param [Array<String>] argv remaining path arguments
51
58
  # @return [Integer] process exit code
52
59
  def run(options:, argv:)
60
+ return run_via_server(options: options, argv: argv) if options[:server]
61
+
62
+ require 'docscribe/inline_rewriter'
53
63
  conf = build_config(options)
54
64
 
55
65
  return run_stdin(options: options, conf: conf) if options[:mode] == :stdin
@@ -60,10 +70,45 @@ module Docscribe
60
70
  run_files(options: options, conf: conf, paths: paths)
61
71
  end
62
72
 
63
- # Load and build the effective config from CLI options.
73
+ # @param [Docscribe::CLI::Formatters::opts] options
74
+ # @param [Array<String>] argv
75
+ # @raise [RuntimeError]
76
+ # @return [Integer]
77
+ # @return [Integer] if RuntimeError
78
+ def run_via_server(options:, argv:)
79
+ require 'docscribe/server'
80
+ conf = build_light_config(options)
81
+ ensure_server_running!(config_path: conf.config_path)
82
+ client = Docscribe::Server::Client.new(config_path: conf.config_path)
83
+ paths = filtered_paths(argv, conf)
84
+ return no_files_found unless paths.any?
85
+
86
+ run_files_via_server(client, paths, options)
87
+ rescue RuntimeError => e
88
+ warn e.message
89
+ 1
90
+ end
91
+
92
+ # Run files through the server client with progress tracking.
64
93
  #
65
- # @param [Docscribe::CLI::Formatters::opts] options parsed CLI options
66
- # @return [Docscribe::Config] effective config with plugins loaded
94
+ # @param [Docscribe::Server::Client] client server client
95
+ # @param [Array<String>] paths file paths to process
96
+ # @param [Docscribe::CLI::Formatters::opts] options CLI options
97
+ # @return [Integer] exit code
98
+ def run_files_via_server(client, paths, options)
99
+ $stdout.sync = true
100
+ state = initial_run_state
101
+ state[:total] = paths.size
102
+ pwd = Pathname.pwd
103
+ paths.each do |path|
104
+ process_one_file_via_server(client, path, options: options, pwd: pwd, state: state)
105
+ end
106
+ finalize_run(options, state)
107
+ run_exit_code(options, state)
108
+ end
109
+
110
+ # @param [Docscribe::CLI::Formatters::opts] options
111
+ # @return [Docscribe::Config]
67
112
  def build_config(options)
68
113
  conf = Docscribe::Config.load(options[:config])
69
114
  conf = Docscribe::CLI::ConfigBuilder.build(conf, options)
@@ -71,13 +116,20 @@ module Docscribe
71
116
  conf
72
117
  end
73
118
 
119
+ # @param [Docscribe::CLI::Formatters::opts] options
120
+ # @return [Docscribe::Config]
121
+ def build_light_config(options)
122
+ conf = Docscribe::Config.load(options[:config])
123
+ Docscribe::CLI::ConfigBuilder.build(conf, options)
124
+ end
125
+
74
126
  # Rewrite code from STDIN using the selected strategy and print the
75
127
  # result.
76
128
  #
77
129
  # @param [Docscribe::CLI::Formatters::opts] options parsed CLI options
78
130
  # @param [Docscribe::Config] conf effective config
79
131
  # @raise [StandardError]
80
- # @return [Integer] if StandardError
132
+ # @return [Integer]
81
133
  # @return [Integer] if StandardError
82
134
  def run_stdin(options:, conf:)
83
135
  puts stdin_rewrite_result(options, conf)[:output]
@@ -127,6 +179,161 @@ module Docscribe
127
179
  2
128
180
  end
129
181
 
182
+ # Ensure the server daemon is running, auto-starting if necessary.
183
+ #
184
+ # @param [String?] config_path
185
+ # @return [void]
186
+ def ensure_server_running!(config_path: nil)
187
+ Docscribe::Server.ensure_running!(config_path: config_path)
188
+ end
189
+
190
+ # Process a single file via the server client.
191
+ #
192
+ # @param [Docscribe::Server::Client] client server client
193
+ # @param [String] path file path
194
+ # @param [Docscribe::CLI::Formatters::opts] options CLI options
195
+ # @param [Pathname] pwd current working directory
196
+ # @param [Docscribe::CLI::Formatters::state] state shared processing state
197
+ # @return [void]
198
+ def process_one_file_via_server(client, path, options:, pwd:, state:)
199
+ display_path = display_path_for(path, pwd: pwd)
200
+ report_progress(state, options, display_path)
201
+ response = send_server_request(client, path, options)
202
+ return server_error(path, state, 'Server unreachable') unless response
203
+ return server_error(path, state, response['error']['message']) if response['error']
204
+
205
+ result = response['result']
206
+ file_changes = (result['changes'] || []).map { |c| symbolize_change(c) }
207
+ dispatch_server_result(result, file_changes, path,
208
+ display_path: display_path, options: options, state: state)
209
+ end
210
+
211
+ # @param [Docscribe::Server::Client] client
212
+ # @param [String] path
213
+ # @param [Docscribe::CLI::Formatters::opts] options
214
+ # @return [Hash<String, Object>, nil]
215
+ def send_server_request(client, path, options)
216
+ method_name = options[:mode] == :write ? :fix : :check
217
+ strategy = options[:strategy].to_s
218
+ cli_overrides = extract_cli_overrides(options)
219
+ if cli_overrides.empty?
220
+ client.send(method_name, file: path, strategy: strategy)
221
+ else
222
+ client.send(method_name, file: path, strategy: strategy, cli_overrides: cli_overrides)
223
+ end
224
+ end
225
+
226
+ # Record a server error in the shared state and print an indicator.
227
+ #
228
+ # @param [String] path file path
229
+ # @param [Docscribe::CLI::Formatters::state] state shared processing state
230
+ # @param [String] message error message
231
+ # @return [void]
232
+ def server_error(path, state, message)
233
+ state[:had_errors] = true
234
+ state[:error_paths] << path
235
+ state[:error_messages][path] = message
236
+ $stderr.print('E')
237
+ end
238
+
239
+ # Dispatch the server result to check or write handler.
240
+ #
241
+ # @param [Hash<String, Object>] result server result with :changed and :changes keys
242
+ # @param [Array<Docscribe::CLI::Formatters::change>] file_changes change records
243
+ # @param [String] path file path
244
+ # @param [Object] ctx context hash with :display_path, :options, :state keys
245
+ # @return [void]
246
+ def dispatch_server_result(result, file_changes, path, **ctx)
247
+ if ctx[:options][:mode] == :check
248
+ handle_via_server_check(path, file_changes: file_changes,
249
+ display_path: ctx[:display_path],
250
+ options: ctx[:options], state: ctx[:state])
251
+ else
252
+ write_server_result(result, file_changes, display_path: ctx[:display_path],
253
+ options: ctx[:options], state: ctx[:state])
254
+ end
255
+ end
256
+
257
+ # Handle a server write-mode result.
258
+ #
259
+ # @param [Hash<String, Object>] result server result with :changed key
260
+ # @param [Array<Docscribe::CLI::Formatters::change>] file_changes change records
261
+ # @param [String] display_path path shown in CLI output
262
+ # @param [Docscribe::CLI::Formatters::opts] options CLI options
263
+ # @param [Docscribe::CLI::Formatters::state] state shared processing state
264
+ # @return [void]
265
+ def write_server_result(result, file_changes, display_path:, options:, state:)
266
+ if result['changed']
267
+ state[:corrected] += 1
268
+ state[:corrected_paths] << display_path
269
+ state[:corrected_changes][display_path] = file_changes
270
+ log_check_verdict('CHANGED', display_path, options)
271
+ else
272
+ log_check_verdict('OK', display_path, options)
273
+ end
274
+ end
275
+
276
+ # Handle a check result from the server.
277
+ #
278
+ # @param [String] path file path
279
+ # @param [Array<Docscribe::CLI::Formatters::change>] file_changes change records from server
280
+ # @param [String] display_path path shown in CLI output
281
+ # @param [Docscribe::CLI::Formatters::opts] options CLI options
282
+ # @param [Docscribe::CLI::Formatters::state] state shared processing state
283
+ # @return [void]
284
+ def handle_via_server_check(path, file_changes:, display_path:, options:, state:)
285
+ if file_changes.empty?
286
+ state[:checked_ok] += 1
287
+ return log_check_verdict('OK', display_path, options)
288
+ end
289
+
290
+ report_check_failure(display_path, file_changes, options)
291
+ update_check_failure_state(path, file_changes, state)
292
+ end
293
+
294
+ # Report a check failure with verbose or compact output.
295
+ #
296
+ # @param [String] display_path path shown in CLI output
297
+ # @param [Array<Docscribe::CLI::Formatters::change>] file_changes change records from server
298
+ # @param [Docscribe::CLI::Formatters::opts] options CLI options
299
+ # @return [void]
300
+ def report_check_failure(display_path, file_changes, options)
301
+ if options[:verbose]
302
+ warn("FAIL #{display_path}")
303
+ print_check_explanations(file_changes)
304
+ else
305
+ $stderr.print('F')
306
+ end
307
+ end
308
+
309
+ # Update shared state after a check failure.
310
+ #
311
+ # @param [String] path file path
312
+ # @param [Array<Docscribe::CLI::Formatters::change>] file_changes change records from server
313
+ # @param [Docscribe::CLI::Formatters::state] state shared processing state
314
+ # @return [void]
315
+ def update_check_failure_state(path, file_changes, state)
316
+ state[:checked_fail] += 1
317
+ state[:changed] = true
318
+ state[:fail_paths] << path
319
+ state[:fail_changes][path] = file_changes
320
+ end
321
+
322
+ # Convert server response change (string keys) to formatter-compatible
323
+ # change (symbol keys).
324
+ #
325
+ # @param [Hash<String, Object>] change change record from server
326
+ # @return [Docscribe::CLI::Formatters::change]
327
+ def symbolize_change(change)
328
+ {
329
+ type: change['type'].to_sym,
330
+ file: change['file'],
331
+ line: change['line'],
332
+ method: change['method'],
333
+ message: change['message']
334
+ }
335
+ end
336
+
130
337
  # Expand CLI path arguments into a sorted list of Ruby files.
131
338
  #
132
339
  # Directories are expanded recursively to `**/*.rb`.
@@ -190,6 +397,20 @@ module Docscribe
190
397
  run_exit_code(options, state)
191
398
  end
192
399
 
400
+ # @param [Docscribe::CLI::Formatters::opts] options
401
+ # @return [Hash<String, Object>]
402
+ def extract_cli_overrides(options)
403
+ overrides = options.slice(*CLI_OVERRIDE_KEYS)
404
+ acc = {} #: Hash[String, untyped]
405
+ overrides.each do |k, v|
406
+ next if v.nil? || v == false
407
+ next if v.is_a?(Array) && v.empty?
408
+
409
+ acc[k.to_s] = v
410
+ end
411
+ acc
412
+ end
413
+
193
414
  private
194
415
 
195
416
  # Print the check or write summary at the end of a run.
@@ -293,7 +514,7 @@ module Docscribe
293
514
  # @param [String] path file path to display
294
515
  # @param [Pathname] pwd current working directory
295
516
  # @raise [StandardError]
296
- # @return [String] if StandardError
517
+ # @return [String]
297
518
  # @return [Object] if StandardError
298
519
  def display_path_for(path, pwd:)
299
520
  abs = Pathname.new(path).expand_path
@@ -315,7 +536,7 @@ module Docscribe
315
536
  # @param [Docscribe::CLI::Formatters::opts] options CLI options
316
537
  # @param [Docscribe::CLI::Formatters::state] state shared processing state
317
538
  # @raise [StandardError]
318
- # @return [String, nil] if StandardError
539
+ # @return [String, nil]
319
540
  # @return [nil] if StandardError
320
541
  def read_source_for_path(path, display_path:, options:, state:)
321
542
  File.read(path)
@@ -334,7 +555,7 @@ module Docscribe
334
555
  # @param [String] src source code
335
556
  # @param [Hash<Symbol, Object>] ctx context hash with :conf, :display_path, :options, :state keys
336
557
  # @raise [StandardError]
337
- # @return [Hash<Symbol, Object>, nil] if StandardError
558
+ # @return [Hash<Symbol, Object>, nil]
338
559
  # @return [nil] if StandardError
339
560
  def rewrite_result_for_path(path, src:, ctx:)
340
561
  conf = ctx[:conf]
@@ -456,7 +677,7 @@ module Docscribe
456
677
  # @param [Array<Docscribe::CLI::Formatters::change>] file_changes structured change records
457
678
  # @param [Object] ctx context hash with :display_path, :options, :state keys
458
679
  # @raise [StandardError]
459
- # @return [void] if StandardError
680
+ # @return [void]
460
681
  # @return [Object] if StandardError
461
682
  def handle_write_result(path, src:, out:, file_changes:, **ctx)
462
683
  return log_check_verdict('OK', ctx[:display_path], ctx[:options]) if out == src
@@ -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
@@ -160,9 +160,9 @@ module Docscribe
160
160
  # @param [Array<Docscribe::CLI::Sigs::MethodDef>] methods
161
161
  # @raise [Parser::SyntaxError]
162
162
  # @raise [StandardError]
163
- # @return [void] if StandardError
164
- # @return [Object] if Parser::SyntaxError
165
- # @return [Object] if StandardError
163
+ # @return [void]
164
+ # @return [nil] if Parser::SyntaxError
165
+ # @return [nil] if StandardError
166
166
  def extract_methods_from_file(path, methods)
167
167
  src = File.read(path)
168
168
  ast = Docscribe::Parsing.parse(src, file: path)
@@ -281,7 +281,7 @@ module Docscribe
281
281
  # @param [Hash<Symbol, Object>] options
282
282
  # @raise [LoadError]
283
283
  # @raise [StandardError]
284
- # @return [Docscribe::Types::RBS::Provider?] if StandardError
284
+ # @return [Docscribe::Types::RBS::Provider?]
285
285
  # @return [nil] if LoadError
286
286
  # @return [nil] if StandardError
287
287
  def build_provider(options)
@@ -298,7 +298,7 @@ module Docscribe
298
298
 
299
299
  # @private
300
300
  # @raise [StandardError]
301
- # @return [Array<String>] if StandardError
301
+ # @return [Array<String>]
302
302
  # @return [Array] if StandardError
303
303
  def load_collection_dirs
304
304
  dir = Docscribe::Types::RBS::CollectionLoader.resolve
data/lib/docscribe/cli.rb CHANGED
@@ -1,27 +1,14 @@
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
- require 'docscribe/cli/sigs'
8
- require 'docscribe/cli/rbs_gen'
9
- require 'docscribe/cli/update_types'
10
- require 'docscribe/cli/check_for_comments'
11
5
 
12
6
  module Docscribe
13
7
  # CLI entry point and command dispatch.
14
8
  module CLI
15
9
  class << self
16
- # Main CLI entry point.
17
- #
18
- # Dispatches:
19
- # - `docscribe init ...` to the config-template generator
20
- # - `docscribe generate ...` to the plugin skeleton generator
21
- # - all other commands to the main option parser and runner
22
- #
23
- # @param [Array<String>] argv raw command-line arguments
24
- # @return [Integer] process exit code
10
+ # @param [Array<String>] argv
11
+ # @return [Integer]
25
12
  def run(argv)
26
13
  argv = argv.dup
27
14
  return dispatch_subcommand(argv) if subcommand?(argv.first)
@@ -30,33 +17,35 @@ module Docscribe
30
17
  Docscribe::CLI::Run.run(options: options, argv: argv)
31
18
  end
32
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
+
33
30
  private
34
31
 
35
- # Subcommand
36
- #
37
32
  # @private
38
- # @param [String?] cmd potential subcommand name
33
+ # @param [String?] cmd
39
34
  # @return [Boolean]
40
35
  def subcommand?(cmd)
41
- %w[init generate sigs rbs update_types check_for_comments].include?(cmd)
36
+ COMMANDS.key?(cmd)
42
37
  end
43
38
 
44
- # Dispatch subcommand
45
- #
46
39
  # @private
47
- # @param [Array<String>] argv raw command-line arguments
40
+ # @param [Array<String>] argv
48
41
  # @return [Integer]
49
42
  def dispatch_subcommand(argv)
50
43
  cmd = argv.shift
51
- case cmd
52
- when 'init' then Docscribe::CLI::Init.run(argv)
53
- when 'generate' then Docscribe::CLI::Generate.run(argv)
54
- when 'sigs' then Docscribe::CLI::Sigs.run(argv)
55
- when 'rbs' then Docscribe::CLI::RbsGen.run(argv)
56
- when 'update_types' then Docscribe::CLI::UpdateTypes.run(argv)
57
- when 'check_for_comments' then Docscribe::CLI::CheckForComments.run(argv)
58
- else 0
59
- end
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)
60
49
  end
61
50
  end
62
51
  end
@@ -70,7 +70,8 @@ module Docscribe
70
70
  'sorbet' => {
71
71
  'enabled' => false,
72
72
  'rbi_dirs' => ['sorbet/rbi', 'rbi'],
73
- 'collapse_generics' => false
73
+ 'collapse_generics' => false,
74
+ 'collapse_object_generics' => false
74
75
  },
75
76
  'keep_descriptions' => false,
76
77
  'skip_anonymous_block_params' => false,
@@ -32,8 +32,8 @@ module Docscribe
32
32
  #
33
33
  # @param [String] path file path to test
34
34
  # @raise [StandardError]
35
+ # @return [String]
35
36
  # @return [String] if StandardError
36
- # @return [Object] if StandardError
37
37
  def relative_path(path)
38
38
  Pathname.new(path).expand_path.relative_path_from(Pathname.pwd).cleanpath.to_s
39
39
  rescue StandardError
@@ -14,12 +14,10 @@ module Docscribe
14
14
  # @return [Docscribe::Config]
15
15
  def self.load(path = nil)
16
16
  raw = {} #: Hash[String, untyped]
17
- if path && File.file?(path)
18
- raw = safe_load_file_compat(path)
19
- elsif File.file?('docscribe.yml')
20
- raw = safe_load_file_compat('docscribe.yml')
21
- end
22
- new(raw)
17
+ resolved = path if path && File.file?(path)
18
+ resolved ||= 'docscribe.yml' if File.file?('docscribe.yml')
19
+ raw = safe_load_file_compat(resolved) if resolved
20
+ new(config_path: resolved, **raw) # steep:ignore
23
21
  end
24
22
 
25
23
  # Safely load YAML from a file across Ruby/Psych versions.
@@ -47,7 +45,7 @@ module Docscribe
47
45
  # @param [String] yaml YAML document
48
46
  # @param [String?] filename optional filename for diagnostics
49
47
  # @raise [ArgumentError]
50
- # @return [Hash<String, Object>] if ArgumentError
48
+ # @return [Hash<String, Object>]
51
49
  # @return [Object] if ArgumentError
52
50
  def self.safe_load_compat(yaml, filename: nil)
53
51
  pclasses = [] #: Array[String]
@@ -62,7 +62,7 @@ module Docscribe
62
62
  #
63
63
  # @private
64
64
  # @raise [LoadError]
65
- # @return [Docscribe::Types::RBS::Provider, nil] if LoadError
65
+ # @return [Docscribe::Types::RBS::Provider, nil]
66
66
  # @return [nil] if LoadError
67
67
  def build_rbs_provider
68
68
  require 'docscribe/types/rbs/provider'
@@ -81,7 +81,7 @@ module Docscribe
81
81
  #
82
82
  # @private
83
83
  # @raise [LoadError]
84
- # @return [Docscribe::Types::RBS::Provider, nil] if LoadError
84
+ # @return [Docscribe::Types::RBS::Provider, nil]
85
85
  # @return [nil] if LoadError
86
86
  def build_core_rbs_provider
87
87
  require 'docscribe/types/rbs/provider'
@@ -40,14 +40,15 @@ module Docscribe
40
40
  # @param [String] source Ruby source being rewritten
41
41
  # @param [String] file source name for diagnostics
42
42
  # @raise [LoadError]
43
- # @return [Docscribe::Types::Sorbet::SourceProvider, nil] if LoadError
43
+ # @return [Docscribe::Types::Sorbet::SourceProvider, nil]
44
44
  # @return [nil] if LoadError
45
45
  def sorbet_source_provider(source, file)
46
46
  require 'docscribe/types/sorbet/source_provider'
47
47
  Docscribe::Types::Sorbet::SourceProvider.new(
48
48
  source: source,
49
49
  file: file,
50
- collapse_generics: sorbet_collapse_generics?
50
+ collapse_generics: sorbet_collapse_generics?,
51
+ collapse_object_generics: sorbet_collapse_object_generics?
51
52
  )
52
53
  rescue LoadError
53
54
  nil
@@ -74,10 +75,9 @@ module Docscribe
74
75
 
75
76
  @sorbet_rbi_provider ||= begin
76
77
  require 'docscribe/types/sorbet/rbi_provider'
77
- Docscribe::Types::Sorbet::RBIProvider.new(
78
- rbi_dirs: sorbet_rbi_dirs,
79
- collapse_generics: sorbet_collapse_generics?
80
- )
78
+ Docscribe::Types::Sorbet::RBIProvider.new(rbi_dirs: sorbet_rbi_dirs,
79
+ collapse_generics: sorbet_collapse_generics?,
80
+ collapse_object_generics: sorbet_collapse_object_generics?)
81
81
  rescue LoadError
82
82
  nil
83
83
  end
@@ -106,5 +106,14 @@ module Docscribe
106
106
  def sorbet_collapse_generics?
107
107
  fetch_bool(%w[sorbet collapse_generics], rbs_collapse_generics?)
108
108
  end
109
+
110
+ # Whether to collapse Object-typed generics in Sorbet types.
111
+ #
112
+ # Falls back to the RBS setting when Sorbet-specific config is not present.
113
+ #
114
+ # @return [Boolean]
115
+ def sorbet_collapse_object_generics?
116
+ fetch_bool(%w[sorbet collapse_object_generics], rbs_collapse_object_generics?)
117
+ end
109
118
  end
110
119
  end