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.
- checksums.yaml +4 -4
- data/README.md +588 -104
- data/lib/docscribe/cli/check_for_comments.rb +183 -0
- data/lib/docscribe/cli/config_builder.rb +180 -36
- data/lib/docscribe/cli/formatters/json.rb +294 -0
- data/lib/docscribe/cli/formatters/sarif.rb +235 -0
- data/lib/docscribe/cli/formatters/text.rb +208 -0
- data/lib/docscribe/cli/formatters.rb +26 -0
- data/lib/docscribe/cli/generate.rb +296 -125
- data/lib/docscribe/cli/init.rb +58 -14
- data/lib/docscribe/cli/options.rb +410 -133
- data/lib/docscribe/cli/rbs_gen.rb +529 -0
- data/lib/docscribe/cli/run.rb +503 -189
- data/lib/docscribe/cli/sigs.rb +366 -0
- data/lib/docscribe/cli/update_types.rb +103 -0
- data/lib/docscribe/cli.rb +35 -9
- data/lib/docscribe/config/defaults.rb +16 -12
- data/lib/docscribe/config/emit.rb +18 -0
- data/lib/docscribe/config/filtering.rb +37 -31
- data/lib/docscribe/config/loader.rb +20 -13
- data/lib/docscribe/config/plugin.rb +2 -1
- data/lib/docscribe/config/rbs.rb +68 -27
- data/lib/docscribe/config/sorbet.rb +40 -17
- data/lib/docscribe/config/sorting.rb +2 -1
- data/lib/docscribe/config/template.rb +10 -1
- data/lib/docscribe/config/utils.rb +12 -9
- data/lib/docscribe/config.rb +3 -4
- data/lib/docscribe/infer/ast_walk.rb +1 -1
- data/lib/docscribe/infer/constants.rb +15 -0
- data/lib/docscribe/infer/literals.rb +39 -26
- data/lib/docscribe/infer/names.rb +24 -16
- data/lib/docscribe/infer/params.rb +57 -13
- data/lib/docscribe/infer/raises.rb +23 -15
- data/lib/docscribe/infer/returns.rb +784 -199
- data/lib/docscribe/infer.rb +28 -28
- data/lib/docscribe/inline_rewriter/collector.rb +816 -430
- data/lib/docscribe/inline_rewriter/doc_block.rb +323 -150
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1837 -648
- data/lib/docscribe/inline_rewriter/source_helpers.rb +119 -71
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +165 -107
- data/lib/docscribe/inline_rewriter.rb +1144 -727
- data/lib/docscribe/parsing.rb +29 -10
- data/lib/docscribe/plugin/base/collector_plugin.rb +3 -3
- data/lib/docscribe/plugin/base/tag_plugin.rb +1 -2
- data/lib/docscribe/plugin/context.rb +28 -18
- data/lib/docscribe/plugin/registry.rb +49 -23
- data/lib/docscribe/plugin/tag.rb +9 -14
- data/lib/docscribe/plugin.rb +54 -22
- data/lib/docscribe/types/provider_chain.rb +4 -2
- data/lib/docscribe/types/rbs/collection_loader.rb +2 -3
- data/lib/docscribe/types/rbs/provider.rb +127 -62
- data/lib/docscribe/types/rbs/type_formatter.rb +286 -77
- data/lib/docscribe/types/signature.rb +22 -42
- data/lib/docscribe/types/sorbet/base_provider.rb +51 -27
- data/lib/docscribe/types/sorbet/rbi_provider.rb +3 -3
- data/lib/docscribe/types/sorbet/source_provider.rb +3 -2
- data/lib/docscribe/types/yard/formatter.rb +100 -0
- data/lib/docscribe/types/yard/parser.rb +240 -0
- data/lib/docscribe/types/yard/types.rb +52 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +34 -2
|
@@ -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] if StandardError
|
|
164
|
+
# @return [Object] if Parser::SyntaxError
|
|
165
|
+
# @return [Object] 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?] if StandardError
|
|
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>] if StandardError
|
|
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
|
@@ -4,8 +4,13 @@ require 'docscribe/cli/init'
|
|
|
4
4
|
require 'docscribe/cli/generate'
|
|
5
5
|
require 'docscribe/cli/options'
|
|
6
6
|
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'
|
|
7
11
|
|
|
8
12
|
module Docscribe
|
|
13
|
+
# CLI entry point and command dispatch.
|
|
9
14
|
module CLI
|
|
10
15
|
class << self
|
|
11
16
|
# Main CLI entry point.
|
|
@@ -19,19 +24,40 @@ module Docscribe
|
|
|
19
24
|
# @return [Integer] process exit code
|
|
20
25
|
def run(argv)
|
|
21
26
|
argv = argv.dup
|
|
22
|
-
|
|
23
|
-
case argv.first
|
|
24
|
-
when 'init'
|
|
25
|
-
argv.shift
|
|
26
|
-
return Docscribe::CLI::Init.run(argv)
|
|
27
|
-
when 'generate'
|
|
28
|
-
argv.shift
|
|
29
|
-
return Docscribe::CLI::Generate.run(argv)
|
|
30
|
-
end
|
|
27
|
+
return dispatch_subcommand(argv) if subcommand?(argv.first)
|
|
31
28
|
|
|
32
29
|
options = Docscribe::CLI::Options.parse!(argv)
|
|
33
30
|
Docscribe::CLI::Run.run(options: options, argv: argv)
|
|
34
31
|
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# Subcommand
|
|
36
|
+
#
|
|
37
|
+
# @private
|
|
38
|
+
# @param [String?] cmd potential subcommand name
|
|
39
|
+
# @return [Boolean]
|
|
40
|
+
def subcommand?(cmd)
|
|
41
|
+
%w[init generate sigs rbs update_types check_for_comments].include?(cmd)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Dispatch subcommand
|
|
45
|
+
#
|
|
46
|
+
# @private
|
|
47
|
+
# @param [Array<String>] argv raw command-line arguments
|
|
48
|
+
# @return [Integer]
|
|
49
|
+
def dispatch_subcommand(argv)
|
|
50
|
+
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
|
|
60
|
+
end
|
|
35
61
|
end
|
|
36
62
|
end
|
|
37
63
|
end
|
|
@@ -33,14 +33,14 @@ module Docscribe
|
|
|
33
33
|
},
|
|
34
34
|
'methods' => {
|
|
35
35
|
'instance' => {
|
|
36
|
-
'public' => {},
|
|
37
|
-
'protected' => {},
|
|
38
|
-
'private' => {}
|
|
36
|
+
'public' => {}, #: Hash[String, untyped]
|
|
37
|
+
'protected' => {}, #: Hash[String, untyped]
|
|
38
|
+
'private' => {} #: Hash[String, untyped]
|
|
39
39
|
},
|
|
40
40
|
'class' => {
|
|
41
|
-
'public' => {},
|
|
42
|
-
'protected' => {},
|
|
43
|
-
'private' => {}
|
|
41
|
+
'public' => {}, #: Hash[String, untyped]
|
|
42
|
+
'protected' => {}, #: Hash[String, untyped]
|
|
43
|
+
'private' => {} #: Hash[String, untyped]
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
'inference' => {
|
|
@@ -51,10 +51,10 @@ module Docscribe
|
|
|
51
51
|
'filter' => {
|
|
52
52
|
'visibilities' => %w[public protected private],
|
|
53
53
|
'scopes' => %w[instance class],
|
|
54
|
-
'include' => [],
|
|
55
|
-
'exclude' => [],
|
|
54
|
+
'include' => [], #: Array[String]
|
|
55
|
+
'exclude' => [], #: Array[String]
|
|
56
56
|
'files' => {
|
|
57
|
-
'include' => [],
|
|
57
|
+
'include' => [], #: Array[String]
|
|
58
58
|
'exclude' => ['spec']
|
|
59
59
|
}
|
|
60
60
|
},
|
|
@@ -62,16 +62,20 @@ module Docscribe
|
|
|
62
62
|
'enabled' => false,
|
|
63
63
|
'collection' => false,
|
|
64
64
|
'sig_dirs' => ['sig'],
|
|
65
|
-
'collection_dirs' => [],
|
|
66
|
-
'collapse_generics' => false
|
|
65
|
+
'collection_dirs' => [], #: Array[String]
|
|
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
73
|
'collapse_generics' => false
|
|
72
74
|
},
|
|
75
|
+
'keep_descriptions' => false,
|
|
76
|
+
'skip_anonymous_block_params' => false,
|
|
73
77
|
'plugins' => {
|
|
74
|
-
'require' => []
|
|
78
|
+
'require' => [] #: Array[String]
|
|
75
79
|
}
|
|
76
80
|
}.freeze
|
|
77
81
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Docscribe
|
|
4
|
+
# Emit-related configuration (headers, visibility tags, etc.).
|
|
4
5
|
class Config
|
|
5
6
|
# Whether to emit method header lines such as:
|
|
6
7
|
# # +MyClass#foo+ -> Integer
|
|
@@ -138,5 +139,22 @@ module Docscribe
|
|
|
138
139
|
def include_param_documentation?
|
|
139
140
|
fetch_bool(%w[emit include_param_documentation], true)
|
|
140
141
|
end
|
|
142
|
+
|
|
143
|
+
# Whether to preserve existing @param/@return descriptions in aggressive mode.
|
|
144
|
+
#
|
|
145
|
+
# @return [Boolean]
|
|
146
|
+
def keep_descriptions?
|
|
147
|
+
fetch_bool(%w[keep_descriptions], false)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Whether to skip @param generation for anonymous block arguments (&).
|
|
151
|
+
#
|
|
152
|
+
# Ruby 3.2+ allows `def foo(&)`. When enabled, no @param is generated
|
|
153
|
+
# for anonymous block parameters since they have no name to reference.
|
|
154
|
+
#
|
|
155
|
+
# @return [Boolean]
|
|
156
|
+
def skip_anonymous_block_params?
|
|
157
|
+
fetch_bool(%w[skip_anonymous_block_params], false)
|
|
158
|
+
end
|
|
141
159
|
end
|
|
142
160
|
end
|