docscribe 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 911fd61018509316b42bf5a799232d24be36cb653d5997c4e170dab5db028884
4
- data.tar.gz: 4ce0e7f0cc78c6b94ce366cb464726e083a6050a42f835e35995c378072507e6
3
+ metadata.gz: 755d272674fe454b80cc008acb50a879da920def321442cdc8cdd7c450b91f52
4
+ data.tar.gz: 92aa2320a36162272acfa2b430bfa9cccf5a4582770885a996bc61d7a4198bb6
5
5
  SHA512:
6
- metadata.gz: 107a67dd5c484b840ba5ab85918ec25d53b0ce4adda13e4ef612d3bfa99d4201e82b474cd636f79a60e5e40e5cbaeb8e15c899939b5e74df14f397f4188b4168
7
- data.tar.gz: 9cac2bb4b0c37b29707f0d6beec99987ab8d0f3d9327b3f44e9684816694e74fced394d88fc493ee6f5dc19ce07b82fc295e38f9c0273faad09976d125103a9e
6
+ metadata.gz: 9268725904b6644b4d46a89c72cda271c4d1b5fedc5066396af1515fa955fecb864fad10406db81582391f81d5265595c7c2bb6fe5793bfc1c7b465395937577
7
+ data.tar.gz: e8012b3c09ec75e8bd7bbed32f51b297edb2d081b65b2bdf849667e1c7cdb5469862697ddc060c8194b4bf08cc671cb61b6b94507ca382841745e27923963513
@@ -44,7 +44,7 @@ module Docscribe
44
44
  raw['filter']['files']['include'] = Array(raw['filter']['files']['include']) + options[:include_file]
45
45
  raw['filter']['files']['exclude'] = Array(raw['filter']['files']['exclude']) + options[:exclude_file]
46
46
 
47
- if options[:rbs] || options[:sig_dirs].any?
47
+ if options[:rbs] || options[:rbs_collection] || options[:sig_dirs].any?
48
48
  raw['rbs'] ||= {}
49
49
  raw['rbs']['enabled'] = true
50
50
  raw['rbs']['sig_dirs'] = Array(raw['rbs']['sig_dirs']) + options[:sig_dirs] if options[:sig_dirs].any?
@@ -53,7 +53,7 @@ module Docscribe
53
53
  require 'docscribe/types/rbs/collection_loader'
54
54
  collection_path = Docscribe::Types::RBS::CollectionLoader.resolve
55
55
  if collection_path
56
- raw['rbs']['sig_dirs'] = Array(raw['rbs']['sig_dirs']) + [collection_path]
56
+ raw['rbs']['collection_dirs'] = Array(raw['rbs']['collection_dirs']) + [collection_path]
57
57
  else
58
58
  warn 'Docscribe: rbs_collection.lock.yaml not found or collection not installed. ' \
59
59
  'Run `bundle exec rbs collection install` first.'
@@ -148,7 +148,9 @@ module Docscribe
148
148
  fail_paths: [],
149
149
  fail_changes: {},
150
150
  error_paths: [],
151
- error_messages: {}
151
+ error_messages: {},
152
+ type_mismatch_paths: [],
153
+ type_mismatch_changes: {}
152
154
  }
153
155
  end
154
156
 
@@ -278,9 +280,18 @@ module Docscribe
278
280
  # @param [Hash] state shared processing state
279
281
  # @return [void]
280
282
  def handle_check_result(path, src:, out:, file_changes:, display_path:, options:, state:)
281
- if out == src
282
- options[:verbose] ? puts("OK #{display_path}") : print('.')
283
- state[:checked_ok] += 1
283
+ type_mismatches = file_changes.select { |c| %i[updated_param updated_return].include?(c[:type]) }
284
+ has_real_changes = file_changes.any? { |c| !%i[updated_param updated_return].include?(c[:type]) }
285
+
286
+ if out == src && !has_real_changes
287
+ if type_mismatches.any?
288
+ state[:type_mismatch_paths] << path
289
+ state[:type_mismatch_changes][path] = type_mismatches
290
+ options[:verbose] ? puts("MT #{display_path}") : print('M')
291
+ else
292
+ state[:checked_ok] += 1
293
+ options[:verbose] ? puts("OK #{display_path}") : print('.')
294
+ end
284
295
  return
285
296
  end
286
297
 
@@ -350,13 +361,22 @@ module Docscribe
350
361
  puts
351
362
 
352
363
  checked_error = state[:error_paths].size
364
+ type_mismatch_count = state[:type_mismatch_paths].size
353
365
 
354
- if state[:checked_fail].zero? && checked_error.zero?
366
+ if state[:checked_fail].zero? && checked_error.zero? && type_mismatch_count.zero?
355
367
  puts "Docscribe: OK (#{state[:checked_ok]} files checked)"
356
368
  return
357
369
  end
358
370
 
359
- puts "Docscribe: FAILED (#{state[:checked_fail]} files need updates, #{checked_error} errors, #{state[:checked_ok]} ok)"
371
+ if state[:checked_fail].zero? && checked_error.zero?
372
+ puts "Docscribe: OK (#{state[:checked_ok]} files checked, #{type_mismatch_count} with type mismatches)"
373
+ else
374
+ parts = ["#{state[:checked_fail]} need updates"]
375
+ parts << "#{type_mismatch_count} type mismatches" if type_mismatch_count.positive?
376
+ parts << "#{checked_error} errors"
377
+ parts << "#{state[:checked_ok]} ok"
378
+ puts "Docscribe: FAILED (#{parts.join(', ')})"
379
+ end
360
380
 
361
381
  state[:fail_paths].each do |p|
362
382
  warn "Would update docs: #{p}"
@@ -367,6 +387,15 @@ module Docscribe
367
387
  end
368
388
  end
369
389
 
390
+ if options[:verbose] || options[:explain]
391
+ state[:type_mismatch_paths].each do |p|
392
+ warn "Type mismatches: #{p}"
393
+ Array(state[:type_mismatch_changes][p]).each do |change|
394
+ warn " - #{format_change_reason(change)}"
395
+ end
396
+ end
397
+ end
398
+
370
399
  state[:error_paths].each do |p|
371
400
  warn "Error processing: #{p}"
372
401
  warn " #{state[:error_messages][p]}" if state[:error_messages][p]
@@ -62,6 +62,7 @@ module Docscribe
62
62
  'enabled' => false,
63
63
  'collection' => false,
64
64
  'sig_dirs' => ['sig'],
65
+ 'collection_dirs' => [],
65
66
  'collapse_generics' => false
66
67
  },
67
68
  'sorbet' => {
@@ -17,6 +17,7 @@ module Docscribe
17
17
  require 'docscribe/types/rbs/provider'
18
18
  Docscribe::Types::RBS::Provider.new(
19
19
  sig_dirs: rbs_sig_dirs,
20
+ collection_dirs: rbs_collection_dirs,
20
21
  collapse_generics: rbs_collapse_generics?
21
22
  )
22
23
  rescue LoadError
@@ -73,6 +74,18 @@ module Docscribe
73
74
  Array(raw.dig('rbs', 'sig_dirs') || DEFAULT.dig('rbs', 'sig_dirs')).map(&:to_s)
74
75
  end
75
76
 
77
+ # RBS collection directories (auto-discovered from rbs_collection.lock.yaml).
78
+ #
79
+ # Loaded separately from user sig_dirs so that collection-related
80
+ # RBS environment errors (e.g. duplicate declarations against core
81
+ # stdlib types) do not silence all RBS lookups.
82
+ #
83
+ # @private
84
+ # @return [Array<String>]
85
+ def rbs_collection_dirs
86
+ Array(raw.dig('rbs', 'collection_dirs')).map(&:to_s)
87
+ end
88
+
76
89
  # Whether generic RBS types should be collapsed to simpler container names.
77
90
  #
78
91
  # Examples:
@@ -86,6 +86,7 @@ module Docscribe
86
86
  # Use RBS signatures for better types (requires `gem "rbs"`)
87
87
  enabled: false
88
88
  sig_dirs: ["sig"]
89
+ collection_dirs: [] # auto-discovered from --rbs-collection
89
90
  collapse_generics: false # Hash<Symbol, String> => Hash
90
91
  collection: false # auto-discover from rbs_collection.lock.yaml
91
92
 
@@ -289,10 +289,10 @@ module Docscribe
289
289
  if !info[:param_names].include?(pname)
290
290
  lines << "#{pl}\n"
291
291
  reasons << { type: :missing_param, message: "missing @param #{pname}", extra: { param: pname } }
292
- elsif info[:param_types][pname] && strategy != :safe
292
+ elsif external_sig && info[:param_types][pname]
293
293
  new_type = extract_param_type_from_param_line(pl)
294
294
  if new_type && info[:param_types][pname] != new_type
295
- lines << "#{pl}\n"
295
+ lines << "#{pl}\n" unless strategy == :safe
296
296
  reasons << {
297
297
  type: :updated_param,
298
298
  message: "updated @param #{pname} from #{info[:param_types][pname]} to #{new_type}",
@@ -318,8 +318,8 @@ module Docscribe
318
318
  if !info[:has_return]
319
319
  lines << "#{indent}# @return [#{normal_type}]\n"
320
320
  reasons << { type: :missing_return, message: 'missing @return' }
321
- elsif info[:return_type] && info[:return_type] != normal_type && strategy != :safe
322
- lines << "#{indent}# @return [#{normal_type}]\n"
321
+ elsif external_sig && info[:return_type] && info[:return_type] != normal_type
322
+ lines << "#{indent}# @return [#{normal_type}]\n" unless strategy == :safe
323
323
  reasons << {
324
324
  type: :updated_return,
325
325
  message: "updated @return from #{info[:return_type]} to #{normal_type}"
@@ -395,24 +395,28 @@ module Docscribe
395
395
  range = Parser::Source::Range.new(buffer, info[:start_pos], info[:end_pos])
396
396
  rewriter.replace(range, new_block)
397
397
 
398
- reason_specs.each do |reason|
398
+ if existing_order_changed
399
399
  add_change(
400
400
  changes,
401
- type: reason[:type],
401
+ type: :unsorted_tags,
402
402
  insertion: insertion,
403
403
  file: file,
404
- message: reason[:message],
405
- extra: reason[:extra] || {}
404
+ message: 'unsorted tags'
406
405
  )
407
406
  end
407
+ end
408
408
 
409
- if existing_order_changed
409
+ type_mismatch_reasons = reason_specs.select { |r| %i[updated_param updated_return].include?(r[:type]) }
410
+
411
+ if new_block != old_block || type_mismatch_reasons.any?
412
+ reason_specs.each do |reason|
410
413
  add_change(
411
414
  changes,
412
- type: :unsorted_tags,
415
+ type: reason[:type],
413
416
  insertion: insertion,
414
417
  file: file,
415
- message: 'unsorted tags'
418
+ message: reason[:message],
419
+ extra: reason[:extra] || {}
416
420
  )
417
421
  end
418
422
  end
@@ -15,16 +15,21 @@ module Docscribe
15
15
  # the pipeline can stay independent of the underlying signature source.
16
16
  class Provider
17
17
  # @param [Array<String>] sig_dirs directories containing `.rbs` files
18
+ # @param [Array<String>] collection_dirs RBS collection directories
19
+ # (loaded separately; on error they are silently dropped and only
20
+ # user sig_dirs are used)
18
21
  # @param [Boolean] collapse_generics whether generic container types
19
22
  # should be simplified during formatting
20
23
  # @return [Object]
21
- def initialize(sig_dirs:, collapse_generics: false)
24
+ def initialize(sig_dirs:, collection_dirs: [], collapse_generics: false)
22
25
  require 'rbs'
23
26
  @sig_dirs = Array(sig_dirs).map(&:to_s)
27
+ @collection_dirs = Array(collection_dirs).map(&:to_s)
24
28
  @collapse_generics = !!collapse_generics
25
29
  @env = nil
26
30
  @builder = nil
27
31
  @warned = false
32
+ @collection_dropped = false
28
33
  end
29
34
 
30
35
  # Look up a normalized method signature from loaded RBS definitions.
@@ -64,22 +69,48 @@ module Docscribe
64
69
 
65
70
  # Lazily load and resolve the RBS environment.
66
71
  #
72
+ # Tries to load collection dirs together with user sig_dirs.
73
+ # If the combined environment raises a load error (e.g. duplicate
74
+ # declarations between collection and core stdlib types), collection
75
+ # dirs are dropped and only user sig_dirs are used.
76
+ #
67
77
  # @private
78
+ # @raise [::RBS::BaseError]
79
+ # @raise [StandardError]
68
80
  # @return [void]
69
81
  def load_env!
70
82
  return if @env && @builder
71
83
 
84
+ @env = build_env(@sig_dirs + @collection_dirs)
85
+ rescue ::RBS::BaseError => e
86
+ raise unless @collection_dirs.any? && !@collection_dropped
87
+
88
+ @collection_dropped = true
89
+ if ENV['DOCSCRIBE_RBS_DEBUG'] == '1'
90
+ warn "Docscribe: RBS collection error (#{e.class}), dropping collection dirs. " \
91
+ 'Set DOCSCRIBE_RBS_DEBUG=1 for details.'
92
+ end
93
+ @env = build_env(@sig_dirs)
94
+ end
95
+
96
+ # Build an RBS environment from the given directories.
97
+ #
98
+ # @private
99
+ # @param [Array<String>] dirs
100
+ # @return [::RBS::Environment]
101
+ def build_env(dirs)
72
102
  loader = ::RBS::EnvironmentLoader.new
73
103
  # Load core types transitively
74
104
  loader.add(library: 'rbs')
75
105
 
76
- @sig_dirs.each do |dir|
106
+ dirs.each do |dir|
77
107
  path = Pathname(dir)
78
108
  loader.add(path: path) if path.directory?
79
109
  end
80
110
 
81
- @env = ::RBS::Environment.from_loader(loader).resolve_type_names
82
- @builder = ::RBS::DefinitionBuilder.new(env: @env)
111
+ env = ::RBS::Environment.from_loader(loader).resolve_type_names
112
+ @builder = ::RBS::DefinitionBuilder.new(env: env)
113
+ env
83
114
  end
84
115
 
85
116
  # Build the appropriate instance or singleton definition for a container.
@@ -89,10 +120,27 @@ module Docscribe
89
120
  # @param [Symbol] scope
90
121
  # @return [Object]
91
122
  def definition_for(container:, scope:)
92
- type_name = ::RBS::TypeName.parse(absolute_const(container))
123
+ type_name = parse_type_name(absolute_const(container))
93
124
  scope == :class ? @builder.build_singleton(type_name) : @builder.build_instance(type_name)
94
125
  end
95
126
 
127
+ # Parse a fully-qualified constant string into an RBS TypeName.
128
+ #
129
+ # Uses the lower-level constructor so it works across RBS versions
130
+ # that may not expose `TypeName.parse`.
131
+ #
132
+ # @private
133
+ # @param [String] string e.g. "::Irb::Autosuggestions"
134
+ # @return [::RBS::TypeName]
135
+ def parse_type_name(string)
136
+ absolute = string.start_with?('::')
137
+ *path, name = string.delete_prefix('::').split('::').map(&:to_sym)
138
+ ::RBS::TypeName.new(
139
+ name: name,
140
+ namespace: ::RBS::Namespace.new(path: path, absolute: absolute)
141
+ )
142
+ end
143
+
96
144
  # Normalize a container name into an absolute constant path.
97
145
  #
98
146
  # @private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Docscribe
4
- VERSION = '1.3.1'
4
+ VERSION = '1.3.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docscribe
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - unurgunite
@@ -213,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
213
213
  - !ruby/object:Gem::Version
214
214
  version: '0'
215
215
  requirements: []
216
- rubygems_version: 4.0.10
216
+ rubygems_version: 4.0.12
217
217
  specification_version: 4
218
218
  summary: Auto-generate inline YARD documentation for Ruby by analyzing code AST. Supports
219
219
  RBS and Sorbet type signatures.