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.
- checksums.yaml +4 -4
- data/README.md +149 -0
- data/lib/docscribe/cli/config_builder.rb +125 -35
- data/lib/docscribe/cli/generate.rb +288 -117
- data/lib/docscribe/cli/init.rb +49 -13
- data/lib/docscribe/cli/options.rb +302 -127
- data/lib/docscribe/cli/run.rb +391 -135
- data/lib/docscribe/cli.rb +23 -5
- data/lib/docscribe/config/defaults.rb +11 -11
- data/lib/docscribe/config/emit.rb +1 -0
- data/lib/docscribe/config/filtering.rb +24 -11
- data/lib/docscribe/config/loader.rb +7 -4
- data/lib/docscribe/config/plugin.rb +1 -0
- data/lib/docscribe/config/rbs.rb +31 -22
- data/lib/docscribe/config/sorbet.rb +41 -15
- data/lib/docscribe/config/sorting.rb +1 -0
- data/lib/docscribe/config/template.rb +1 -0
- data/lib/docscribe/config/utils.rb +1 -0
- data/lib/docscribe/config.rb +1 -0
- data/lib/docscribe/infer/constants.rb +15 -0
- data/lib/docscribe/infer/literals.rb +43 -25
- data/lib/docscribe/infer/names.rb +24 -15
- data/lib/docscribe/infer/params.rb +52 -6
- data/lib/docscribe/infer/raises.rb +24 -14
- data/lib/docscribe/infer/returns.rb +365 -182
- data/lib/docscribe/infer.rb +10 -9
- data/lib/docscribe/inline_rewriter/collector.rb +766 -375
- data/lib/docscribe/inline_rewriter/doc_block.rb +217 -74
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1488 -602
- data/lib/docscribe/inline_rewriter/source_helpers.rb +100 -52
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +109 -48
- data/lib/docscribe/inline_rewriter.rb +1009 -595
- data/lib/docscribe/plugin/base/collector_plugin.rb +2 -3
- data/lib/docscribe/plugin/base/tag_plugin.rb +1 -1
- data/lib/docscribe/plugin/registry.rb +34 -7
- data/lib/docscribe/plugin.rb +48 -17
- data/lib/docscribe/types/rbs/collection_loader.rb +0 -1
- data/lib/docscribe/types/rbs/provider.rb +75 -26
- data/lib/docscribe/types/rbs/type_formatter.rb +127 -59
- data/lib/docscribe/types/sorbet/base_provider.rb +31 -12
- data/lib/docscribe/version.rb +1 -1
- metadata +2 -2
|
@@ -18,6 +18,73 @@ module Docscribe
|
|
|
18
18
|
module DocBuilder
|
|
19
19
|
module_function
|
|
20
20
|
|
|
21
|
+
PARAM_TYPE_COLLECTORS = {
|
|
22
|
+
arg: lambda { |arg_node, param_types, external_sig, config|
|
|
23
|
+
collect_param_type(
|
|
24
|
+
arg_node,
|
|
25
|
+
param_types,
|
|
26
|
+
external_sig,
|
|
27
|
+
config,
|
|
28
|
+
infer_name: nil
|
|
29
|
+
)
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
optarg: lambda { |arg_node, param_types, external_sig, config|
|
|
33
|
+
collect_optarg_param_type(
|
|
34
|
+
arg_node,
|
|
35
|
+
param_types,
|
|
36
|
+
external_sig,
|
|
37
|
+
config,
|
|
38
|
+
infer_name: nil
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
kwarg: lambda { |arg_node, param_types, external_sig, config|
|
|
43
|
+
collect_param_type(
|
|
44
|
+
arg_node,
|
|
45
|
+
param_types,
|
|
46
|
+
external_sig,
|
|
47
|
+
config,
|
|
48
|
+
infer_name: ->(param_name) { "#{param_name}:" }
|
|
49
|
+
)
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
kwoptarg: lambda { |arg_node, param_types, external_sig, config|
|
|
53
|
+
collect_optarg_param_type(
|
|
54
|
+
arg_node,
|
|
55
|
+
param_types,
|
|
56
|
+
external_sig,
|
|
57
|
+
config,
|
|
58
|
+
infer_name: ->(param_name) { "#{param_name}:" }
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
}.freeze
|
|
62
|
+
|
|
63
|
+
PARAM_BUILDERS = {
|
|
64
|
+
arg: lambda { |arg_node, indent, external_sig, param_types_override, **opts|
|
|
65
|
+
[build_arg_line(arg_node, indent, external_sig, param_types_override, **opts)]
|
|
66
|
+
},
|
|
67
|
+
optarg: lambda { |arg_node, indent, external_sig, param_types_override, **opts|
|
|
68
|
+
build_optarg_lines(arg_node, indent, external_sig, param_types_override, **opts)
|
|
69
|
+
},
|
|
70
|
+
kwarg: lambda { |arg_node, indent, external_sig, param_types_override, **opts|
|
|
71
|
+
[build_kwarg_line(arg_node, indent, external_sig, param_types_override, **opts)]
|
|
72
|
+
},
|
|
73
|
+
kwoptarg: lambda { |arg_node, indent, external_sig, param_types_override, **opts|
|
|
74
|
+
[build_kwoptarg_line(arg_node, indent, external_sig, param_types_override, **opts)]
|
|
75
|
+
},
|
|
76
|
+
restarg: lambda { |arg_node, indent, external_sig, param_types_override, **opts|
|
|
77
|
+
[build_restarg_line(arg_node, indent, external_sig, param_types_override, **opts)]
|
|
78
|
+
},
|
|
79
|
+
kwrestarg: lambda { |arg_node, indent, external_sig, param_types_override, **opts|
|
|
80
|
+
[build_kwrestarg_line(arg_node, indent, external_sig, param_types_override, **opts)]
|
|
81
|
+
},
|
|
82
|
+
blockarg: lambda { |arg_node, indent, external_sig, param_types_override, **opts|
|
|
83
|
+
[build_blockarg_line(arg_node, indent, external_sig, param_types_override, **opts)]
|
|
84
|
+
},
|
|
85
|
+
forward_arg: ->(*) { [] } #: Array[String]
|
|
86
|
+
}.freeze
|
|
87
|
+
|
|
21
88
|
# Build a complete doc block for one collected method insertion.
|
|
22
89
|
#
|
|
23
90
|
# External signatures, when available, override inferred param and return
|
|
@@ -26,662 +93,1279 @@ module Docscribe
|
|
|
26
93
|
# @note module_function: when included, also defines #build (instance visibility: private)
|
|
27
94
|
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
28
95
|
# @param [Docscribe::Config] config
|
|
29
|
-
# @param [
|
|
30
|
-
# `signature_for(container:, scope:, name:)`
|
|
31
|
-
# @param [nil] core_rbs_provider Param documentation.
|
|
32
|
-
# @param [nil] param_types Param documentation.
|
|
33
|
-
# @param [nil] return_type_override Param documentation.
|
|
34
|
-
# @param [nil] override_tags Param documentation.
|
|
96
|
+
# @param [Hash] opts additional keyword options forwarded to doc_setup
|
|
35
97
|
# @raise [StandardError]
|
|
36
98
|
# @return [String, nil]
|
|
37
|
-
def build(insertion, config:,
|
|
99
|
+
def build(insertion, config:, **opts)
|
|
100
|
+
setup = doc_setup(insertion, config: config, **opts)
|
|
101
|
+
return nil unless setup
|
|
102
|
+
|
|
103
|
+
build_unsafe(insertion, config: config, setup: setup, **opts)
|
|
104
|
+
rescue StandardError => e
|
|
105
|
+
debug_warn(e, insertion: insertion, name: '(unknown)', phase: 'DocBuilder.build')
|
|
106
|
+
nil
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Build only the missing doc lines that should be merged into an existing
|
|
110
|
+
# doc-like block.
|
|
111
|
+
#
|
|
112
|
+
# This is used by safe mode for non-destructive updates.
|
|
113
|
+
#
|
|
114
|
+
# @note module_function: when included, also defines #build_merge_additions (instance visibility: private)
|
|
115
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
116
|
+
# @param [Array<String>] existing_lines
|
|
117
|
+
# @param [Docscribe::Config] config
|
|
118
|
+
# @param [Hash] options additional keyword options forwarded to downstream methods
|
|
119
|
+
# @raise [StandardError]
|
|
120
|
+
# @return [String, nil]
|
|
121
|
+
def build_merge_additions(insertion, existing_lines:, config:, **options)
|
|
122
|
+
setup = doc_setup(insertion, config: config, **options)
|
|
123
|
+
return '' unless setup
|
|
124
|
+
|
|
125
|
+
info = parse_existing_doc_tags(existing_lines)
|
|
126
|
+
merge_dest_lines(existing_lines, setup: setup, insertion: insertion, config: config, info: info,
|
|
127
|
+
param_types: options[:param_types])
|
|
128
|
+
rescue StandardError => e
|
|
129
|
+
debug_warn(e, insertion: insertion, name: setup&.dig(:name) || '(unknown)',
|
|
130
|
+
phase: 'DocBuilder.build_merge_additions')
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Build structured missing-line information for safe merge mode.
|
|
135
|
+
#
|
|
136
|
+
# Returns both:
|
|
137
|
+
# - generated missing lines
|
|
138
|
+
# - structured reasons used by `--explain`
|
|
139
|
+
#
|
|
140
|
+
# @note module_function: when included, also defines #build_missing_merge_result (instance visibility: private)
|
|
141
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
142
|
+
# @param [Array<String>] existing_lines
|
|
143
|
+
# @param [Docscribe::Config] config
|
|
144
|
+
# @param [Hash] options additional keyword options forwarded to downstream methods
|
|
145
|
+
# @raise [StandardError]
|
|
146
|
+
# @return [Hash]
|
|
147
|
+
def build_missing_merge_result(insertion, existing_lines:, config:, **options)
|
|
148
|
+
setup = doc_setup(insertion, config: config, **options)
|
|
149
|
+
return { lines: [], reasons: [] } unless setup
|
|
150
|
+
|
|
151
|
+
info = parse_existing_doc_tags(existing_lines)
|
|
152
|
+
collect_all_missing(setup, info, insertion, config, options)
|
|
153
|
+
rescue StandardError => e
|
|
154
|
+
debug_warn(e, insertion: insertion, name: setup&.dig(:name) || '(unknown)',
|
|
155
|
+
phase: 'DocBuilder.build_missing_merge_result')
|
|
156
|
+
{ lines: [], reasons: [] }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Shared document setup extraction for all build methods.
|
|
160
|
+
#
|
|
161
|
+
# @note module_function: when included, also defines #doc_setup (instance visibility: private)
|
|
162
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
163
|
+
# @param [Docscribe::Config] config
|
|
164
|
+
# @param [Hash] opts additional options
|
|
165
|
+
# @return [Hash, nil]
|
|
166
|
+
def doc_setup(insertion, config:, **opts)
|
|
38
167
|
node = insertion.node
|
|
39
168
|
name = SourceHelpers.node_name(node)
|
|
40
169
|
return nil unless name
|
|
41
170
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
container = insertion.container
|
|
46
|
-
method_symbol = scope == :instance ? '#' : '.'
|
|
171
|
+
setup = extract_base_setup(insertion, name)
|
|
172
|
+
resolve_doc_setup!(setup, node, name, config, opts)
|
|
173
|
+
end
|
|
47
174
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
175
|
+
# Build without rescue wrapping (extracted for metric reduction).
|
|
176
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
177
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
178
|
+
# @param [Docscribe::Config] config
|
|
179
|
+
# @param [Hash] setup
|
|
180
|
+
# @param [Hash] opts
|
|
181
|
+
# @return [String, nil]
|
|
182
|
+
def build_unsafe(insertion, config:, setup:, **opts)
|
|
183
|
+
_, pl, rt = build_param_and_raise_info(setup, config, opts)
|
|
184
|
+
lines = build_doc_lines(setup, config: config, insertion: insertion, params_lines: pl, raise_types: rt,
|
|
185
|
+
override_tags: opts[:override_tags])
|
|
186
|
+
lines.map { |l| "#{l}\n" }.join
|
|
187
|
+
end
|
|
53
188
|
|
|
54
|
-
|
|
55
|
-
|
|
189
|
+
# Build param types, param lines, and raise types for doc block.
|
|
190
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
191
|
+
# @param [Hash] setup
|
|
192
|
+
# @param [Docscribe::Config] config
|
|
193
|
+
# @param [Hash] opts
|
|
194
|
+
# @return [Array]
|
|
195
|
+
def build_param_and_raise_info(setup, config, opts)
|
|
196
|
+
pt = opts[:param_types] || build_param_types_from_node(setup[:node], external_sig: setup[:external_sig],
|
|
197
|
+
config: config)
|
|
198
|
+
pl = if config.emit_param_tags?
|
|
199
|
+
build_params_lines(setup[:node], setup[:indent], external_sig: setup[:external_sig], config: config,
|
|
200
|
+
param_types_override: pt)
|
|
201
|
+
end
|
|
202
|
+
rt = config.emit_raise_tags? ? Docscribe::Infer.infer_raises_from_node(setup[:node]) : [] #: Array[String]
|
|
203
|
+
[pt, pl, rt]
|
|
204
|
+
end
|
|
56
205
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
206
|
+
# Resolve external signature, returns spec, and normal type for doc setup.
|
|
207
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
208
|
+
# @param [Hash] setup
|
|
209
|
+
# @param [Parser::AST::Node] node
|
|
210
|
+
# @param [String] name
|
|
211
|
+
# @param [Docscribe::Config] config
|
|
212
|
+
# @param [Hash] opts
|
|
213
|
+
# @return [Hash]
|
|
214
|
+
def resolve_doc_setup!(setup, node, name, config, opts)
|
|
215
|
+
external_sig = resolve_external_sig(setup[:container], setup[:scope], name, opts[:signature_provider])
|
|
216
|
+
returns_spec = compute_returns_spec(node, config, opts[:param_types], opts[:core_rbs_provider])
|
|
217
|
+
normal_type = opts[:return_type_override] || external_sig&.return_type || returns_spec[:normal]
|
|
218
|
+
|
|
219
|
+
setup.merge(
|
|
220
|
+
external_sig: external_sig,
|
|
221
|
+
normal_type: normal_type,
|
|
222
|
+
rescue_specs: returns_spec[:rescues] || []
|
|
68
223
|
)
|
|
224
|
+
end
|
|
69
225
|
|
|
70
|
-
|
|
71
|
-
|
|
226
|
+
# Extract base node metadata.
|
|
227
|
+
#
|
|
228
|
+
# @note module_function: when included, also defines #extract_base_setup (instance visibility: private)
|
|
229
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
230
|
+
# @param [String] name
|
|
231
|
+
# @return [Hash]
|
|
232
|
+
def extract_base_setup(insertion, name)
|
|
233
|
+
n = insertion.node
|
|
234
|
+
{ node: n, name: name, indent: SourceHelpers.line_indent(n), scope: insertion.scope,
|
|
235
|
+
visibility: insertion.visibility, container: insertion.container,
|
|
236
|
+
method_symbol: insertion.scope == :instance ? '#' : '.' }
|
|
237
|
+
end
|
|
72
238
|
|
|
73
|
-
|
|
239
|
+
# Resolve external signature.
|
|
240
|
+
#
|
|
241
|
+
# @note module_function: when included, also defines #resolve_external_sig (instance visibility: private)
|
|
242
|
+
# @param [String] container
|
|
243
|
+
# @param [Symbol] scope
|
|
244
|
+
# @param [String] name
|
|
245
|
+
# @param [Object, nil] signature_provider
|
|
246
|
+
# @return [Object, nil]
|
|
247
|
+
def resolve_external_sig(container, scope, name, signature_provider)
|
|
248
|
+
signature_provider&.signature_for(container: container, scope: scope, name: name)
|
|
249
|
+
end
|
|
74
250
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
251
|
+
# Compute returns_spec from node.
|
|
252
|
+
#
|
|
253
|
+
# @note module_function: when included, also defines #compute_returns_spec (instance visibility: private)
|
|
254
|
+
# @param [Parser::AST::Node] node
|
|
255
|
+
# @param [Docscribe::Config] config
|
|
256
|
+
# @param [Hash, nil] param_types
|
|
257
|
+
# @param [Object, nil] core_rbs_provider
|
|
258
|
+
# @return [Hash]
|
|
259
|
+
def compute_returns_spec(node, config, param_types, core_rbs_provider)
|
|
260
|
+
Docscribe::Infer.returns_spec_from_node(
|
|
261
|
+
node, fallback_type: config.fallback_type, nil_as_optional: config.nil_as_optional?,
|
|
262
|
+
param_types: param_types, core_rbs_provider: core_rbs_provider
|
|
263
|
+
)
|
|
264
|
+
end
|
|
79
265
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
266
|
+
# Parse existing doc comment lines and extract known YARD tags.
|
|
267
|
+
#
|
|
268
|
+
# Extracts: `@param` names, `@return`, `@raise`, `@private`, `@protected`,
|
|
269
|
+
# `@module_function` notes, and `@option` lines.
|
|
270
|
+
#
|
|
271
|
+
# @note module_function: when included, also defines #parse_existing_doc_tags (instance visibility: private)
|
|
272
|
+
# @param [Array<String>] lines existing doc comment lines
|
|
273
|
+
# @return [Hash] parsed tag info
|
|
274
|
+
def parse_existing_doc_tags(lines)
|
|
275
|
+
init = init_parse_info
|
|
276
|
+
Array(lines).each_with_object(init) do |line, info|
|
|
277
|
+
extract_param_info(line, info[:param_names], info[:param_types])
|
|
278
|
+
extract_return_info(line, info)
|
|
279
|
+
extract_visibility_info(line, info)
|
|
280
|
+
extract_raise_info(line, info[:raise_types])
|
|
281
|
+
extract_plugin_info(line, info[:plugin_tags])
|
|
83
282
|
end
|
|
283
|
+
end
|
|
84
284
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
285
|
+
# Initialize an empty parse info hash.
|
|
286
|
+
#
|
|
287
|
+
# @note module_function: when included, also defines #init_parse_info (instance visibility: private)
|
|
288
|
+
# @return [Hash]
|
|
289
|
+
def init_parse_info
|
|
290
|
+
{
|
|
291
|
+
param_names: {}, param_types: {}, raise_types: {}, plugin_tags: {},
|
|
292
|
+
has_return: false, return_type: nil,
|
|
293
|
+
has_private: false, has_protected: false, has_module_function_note: false
|
|
294
|
+
}
|
|
295
|
+
end
|
|
93
296
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
297
|
+
# Build merged destination lines for safe merge mode.
|
|
298
|
+
# Wrapper that delegates to merge_lines_with_context.
|
|
299
|
+
#
|
|
300
|
+
# @note module_function: when included, also defines #merge_dest_lines (instance visibility: private)
|
|
301
|
+
# @param [Object] existing_lines existing doc comment lines to merge into
|
|
302
|
+
# @param [Hash] ctx merge context hash (setup, insertion, config, info, param_types)
|
|
303
|
+
# @return [Object]
|
|
304
|
+
def merge_dest_lines(existing_lines, **ctx)
|
|
305
|
+
merge_lines_with_context(existing_lines, **ctx)
|
|
306
|
+
end
|
|
101
307
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
308
|
+
# Merge dest lines using a context hash (extracted for metric reduction).
|
|
309
|
+
#
|
|
310
|
+
# @note module_function: when included, also defines #merge_lines_with_context (instance visibility: private)
|
|
311
|
+
# @param [Object] existing_lines existing doc comment lines being merged
|
|
312
|
+
# @param [Hash] ctx merge context (setup, insertion, config, info, param_types)
|
|
313
|
+
# @return [Object]
|
|
314
|
+
def merge_lines_with_context(existing_lines, **ctx)
|
|
315
|
+
s = ctx[:setup]
|
|
316
|
+
i = s[:indent]
|
|
317
|
+
config = ctx[:config]
|
|
318
|
+
info = ctx[:info]
|
|
319
|
+
base_ary = build_initial_line_ary(existing_lines, i)
|
|
320
|
+
line_ary = merge_all_tag_lines(base_ary, s: s, i: i, config: config, info: info,
|
|
321
|
+
insertion: ctx[:insertion], param_types: ctx[:param_types])
|
|
322
|
+
useful = line_ary.reject { |l| l.strip == '#' }
|
|
323
|
+
return '' if useful.empty?
|
|
105
324
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
lines << "#{indent}# @return [#{normal_type}]" if config.emit_return_tag?(scope, visibility)
|
|
325
|
+
line_ary.map { |l| "#{l}\n" }.join
|
|
326
|
+
end
|
|
109
327
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
328
|
+
# Build initial line array for merge dest lines.
|
|
329
|
+
#
|
|
330
|
+
# @note module_function: when included, also defines #build_initial_line_ary (instance visibility: private)
|
|
331
|
+
# @param [Array<String>] existing_lines
|
|
332
|
+
# @param [String] indent
|
|
333
|
+
# @return [Array<String>]
|
|
334
|
+
def build_initial_line_ary(existing_lines, indent)
|
|
335
|
+
existing_lines.any? && existing_lines.last.strip != '#' ? ["#{indent}#"] : []
|
|
336
|
+
end
|
|
117
337
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
338
|
+
# Merge all tag lines into a line array.
|
|
339
|
+
#
|
|
340
|
+
# @note module_function: when included, also defines #merge_all_tag_lines (instance visibility: private)
|
|
341
|
+
# @param [Array<String>] base_ary
|
|
342
|
+
# @param [Hash] ctx context hash with setup, config, info, insertion, param_types
|
|
343
|
+
# @return [Array<String>]
|
|
344
|
+
def merge_all_tag_lines(base_ary, **ctx)
|
|
345
|
+
line_ary = base_ary.dup
|
|
346
|
+
merge_tag_lines_core(line_ary, ctx)
|
|
347
|
+
line_ary.concat(merge_rescue_return_lines(ctx[:i], ctx[:s][:rescue_specs], ctx[:config], ctx[:info]))
|
|
348
|
+
line_ary
|
|
123
349
|
end
|
|
124
350
|
|
|
125
|
-
#
|
|
126
|
-
# doc-like block.
|
|
351
|
+
# Core tag line merging.
|
|
127
352
|
#
|
|
128
|
-
#
|
|
353
|
+
# @note module_function: when included, also defines #merge_tag_lines_core (instance visibility: private)
|
|
354
|
+
# @param [Array<String>] line_ary
|
|
355
|
+
# @param [Hash] ctx
|
|
356
|
+
# @return [void]
|
|
357
|
+
def merge_tag_lines_core(line_ary, ctx)
|
|
358
|
+
append_merge_tag_lines(line_ary, ctx)
|
|
359
|
+
merge_return_line(line_ary, ctx[:i], ctx[:s], ctx[:config], ctx[:info])
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Append merge tag lines into line_ary.
|
|
129
363
|
#
|
|
130
|
-
# @note module_function: when included, also defines #
|
|
131
|
-
# @param [
|
|
132
|
-
# @param [
|
|
133
|
-
# @
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
# @param [nil] return_type_override Param documentation.
|
|
138
|
-
# @raise [StandardError]
|
|
139
|
-
# @return [String, nil]
|
|
140
|
-
def build_merge_additions(insertion, existing_lines:, config:, signature_provider: nil, core_rbs_provider: nil,
|
|
141
|
-
param_types: nil, return_type_override: nil)
|
|
142
|
-
node = insertion.node
|
|
143
|
-
name = SourceHelpers.node_name(node)
|
|
144
|
-
return '' unless name
|
|
364
|
+
# @note module_function: when included, also defines #append_merge_tag_lines (instance visibility: private)
|
|
365
|
+
# @param [Array<String>] line_ary
|
|
366
|
+
# @param [Hash] ctx
|
|
367
|
+
# @return [void]
|
|
368
|
+
def append_merge_tag_lines(line_ary, ctx)
|
|
369
|
+
line_ary.concat(build_all_merge_tags(ctx))
|
|
370
|
+
end
|
|
145
371
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
372
|
+
# Build an array of all merge tag lines.
|
|
373
|
+
#
|
|
374
|
+
# @note module_function: when included, also defines #build_all_merge_tags (instance visibility: private)
|
|
375
|
+
# @param [Hash] ctx
|
|
376
|
+
# @return [Array<String>]
|
|
377
|
+
def build_all_merge_tags(ctx)
|
|
378
|
+
i = ctx[:i]
|
|
379
|
+
s = ctx[:s]
|
|
380
|
+
c = ctx[:config]
|
|
381
|
+
info = ctx[:info]
|
|
382
|
+
[merge_visibility_tag_lines(i, s[:visibility], c, info),
|
|
383
|
+
merge_module_function_note_lines(i, ctx[:insertion], s[:name], info),
|
|
384
|
+
merge_param_lines(s[:node], i, config: c, external_sig: s[:external_sig],
|
|
385
|
+
param_types: ctx[:param_types], info: info),
|
|
386
|
+
merge_raise_tag_lines(s[:node], i, c, info)].flatten
|
|
387
|
+
end
|
|
150
388
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
389
|
+
# Merge return tag line into line_ary.
|
|
390
|
+
#
|
|
391
|
+
# @note module_function: when included, also defines #merge_return_line (instance visibility: private)
|
|
392
|
+
# @param [Array<String>] line_ary
|
|
393
|
+
# @param [Object] config
|
|
394
|
+
# @param [Object] info
|
|
395
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
396
|
+
# @param [Object] setup method setup hash with node, name, types, scope
|
|
397
|
+
# @return [void]
|
|
398
|
+
def merge_return_line(line_ary, indent, setup, config, info)
|
|
399
|
+
emit_ret = config.emit_return_tag?(setup[:scope], setup[:visibility])
|
|
400
|
+
ret_line = merge_return_tag_line(indent, setup[:normal_type], config: config, scope: setup[:scope],
|
|
401
|
+
visibility: setup[:visibility], info: info)
|
|
156
402
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
fallback_type: config.fallback_type,
|
|
160
|
-
nil_as_optional: config.nil_as_optional?,
|
|
161
|
-
param_types: param_types,
|
|
162
|
-
core_rbs_provider: core_rbs_provider
|
|
163
|
-
)
|
|
403
|
+
line_ary << ret_line if emit_ret && ret_line
|
|
404
|
+
end
|
|
164
405
|
|
|
165
|
-
|
|
166
|
-
|
|
406
|
+
# Collect all missing doc elements and return { lines:, reasons: }.
|
|
407
|
+
# Delegates to collect_missing_all with a merged context hash.
|
|
408
|
+
#
|
|
409
|
+
# @note module_function: when included, also defines #collect_all_missing (instance visibility: private)
|
|
410
|
+
# @param [Object] setup resolved setup hash with node, name, indent, types
|
|
411
|
+
# @param [Object] info parsed existing doc tag information
|
|
412
|
+
# @param [Object] insertion the collected method insertion object
|
|
413
|
+
# @param [Object] config Docscribe configuration object
|
|
414
|
+
# @param [Object] options additional options hash forwarded to missing collector
|
|
415
|
+
# @return [Hash]
|
|
416
|
+
def collect_all_missing(setup, info, insertion, config, options)
|
|
417
|
+
s = setup
|
|
418
|
+
ctx = { node: s[:node], indent: s[:indent], config: config, external_sig: s[:external_sig],
|
|
419
|
+
info: info, strategy: options[:strategy], scope: s[:scope], visibility: s[:visibility],
|
|
420
|
+
normal_type: s[:normal_type], rescue_specs: s[:rescue_specs], insertion: insertion,
|
|
421
|
+
param_types: options[:param_types], override_tags: options[:override_tags] }
|
|
422
|
+
collect_missing_all(ctx)
|
|
423
|
+
end
|
|
167
424
|
|
|
168
|
-
|
|
169
|
-
|
|
425
|
+
# Collect all missing elements via context hash.
|
|
426
|
+
#
|
|
427
|
+
# @note module_function: when included, also defines #collect_missing_all (instance visibility: private)
|
|
428
|
+
# @param [Hash] ctx
|
|
429
|
+
# @return [Hash]
|
|
430
|
+
def collect_missing_all(ctx)
|
|
431
|
+
lines = [] #: Array[String]
|
|
432
|
+
reasons = [] #: Array[Hash]
|
|
433
|
+
collect_missing_visibility!(lines, reasons, **ctx)
|
|
434
|
+
collect_missing_module_function_note!(lines, reasons, **ctx)
|
|
435
|
+
collect_missing_params!(lines, reasons, **ctx)
|
|
436
|
+
collect_missing_raises!(lines, reasons, **ctx)
|
|
437
|
+
collect_missing_return!(lines, reasons, **ctx)
|
|
438
|
+
collect_missing_rescue_returns!(lines, reasons, **ctx)
|
|
439
|
+
collect_missing_plugin_tags!(lines, reasons, **ctx)
|
|
440
|
+
{ lines: lines, reasons: reasons }
|
|
441
|
+
end
|
|
170
442
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
443
|
+
# Extract param info from a doc line.
|
|
444
|
+
# Parses @param lines and populates param_names and param_types hashes.
|
|
445
|
+
#
|
|
446
|
+
# @note module_function: when included, also defines #extract_param_info (instance visibility: private)
|
|
447
|
+
# @param [Object] line a single doc comment line to parse
|
|
448
|
+
# @param [Object] param_names hash tracking existing @param names
|
|
449
|
+
# @param [Object] param_types hash tracking existing @param types
|
|
450
|
+
# @return [Object]
|
|
451
|
+
def extract_param_info(line, param_names, param_types)
|
|
452
|
+
return unless (pname = extract_param_name_from_param_line(line))
|
|
453
|
+
|
|
454
|
+
param_names[pname] = true
|
|
455
|
+
unless (type_match = line.match(/@param\s+\[([^\]]+)\]\s+\S+/) || line.match(/@param\s+\S+\s+\[([^\]]+)\]/))
|
|
456
|
+
return
|
|
177
457
|
end
|
|
178
458
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
459
|
+
param_types[pname] = type_match[1] || 'untyped'
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Extract return info from a doc line.
|
|
463
|
+
# Detects @return tags and records type and presence in info hash.
|
|
464
|
+
#
|
|
465
|
+
# @note module_function: when included, also defines #extract_return_info (instance visibility: private)
|
|
466
|
+
# @param [Object] line a single doc comment line to parse
|
|
467
|
+
# @param [Object] info parse info hash to update with return data
|
|
468
|
+
# @return [Object]
|
|
469
|
+
def extract_return_info(line, info)
|
|
470
|
+
return unless line.match?(/^\s*#\s*@return\b/)
|
|
471
|
+
|
|
472
|
+
info[:has_return] = true
|
|
473
|
+
return unless (m = line.match(/@return\s+\[([^\]]+)\]/))
|
|
474
|
+
|
|
475
|
+
info[:return_type] = m[1]
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# Extract visibility info from a doc line.
|
|
479
|
+
# Detects @private, @protected, and @note module_function tags.
|
|
480
|
+
#
|
|
481
|
+
# @note module_function: when included, also defines #extract_visibility_info (instance visibility: private)
|
|
482
|
+
# @param [Object] line a single doc comment line to parse
|
|
483
|
+
# @param [Object] info parse info hash to update with visibility flags
|
|
484
|
+
# @return [Object]
|
|
485
|
+
def extract_visibility_info(line, info)
|
|
486
|
+
info[:has_private] ||= line.match?(/^\s*#\s*@private\b/)
|
|
487
|
+
info[:has_protected] ||= line.match?(/^\s*#\s*@protected\b/)
|
|
488
|
+
info[:has_module_function_note] ||= line.match?(/^\s*#\s*@note\s+module_function:/)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# Extract raise info from a doc line.
|
|
492
|
+
# Parses @raise tags and records exception types in raise_types hash.
|
|
493
|
+
#
|
|
494
|
+
# @note module_function: when included, also defines #extract_raise_info (instance visibility: private)
|
|
495
|
+
# @param [Object] line a single doc comment line to parse
|
|
496
|
+
# @param [Object] raise_types hash tracking existing @raise types
|
|
497
|
+
# @return [Object]
|
|
498
|
+
def extract_raise_info(line, raise_types)
|
|
499
|
+
extract_raise_types_from_line(line).each { |t| raise_types[t || ''] = true }
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Extract plugin tag info from a doc line.
|
|
503
|
+
# Captures any @tag_name from the line into the plugin_tags hash.
|
|
504
|
+
#
|
|
505
|
+
# @note module_function: when included, also defines #extract_plugin_info (instance visibility: private)
|
|
506
|
+
# @param [Object] line a single doc comment line to parse
|
|
507
|
+
# @param [Object] plugin_tags hash tracking existing plugin tag names
|
|
508
|
+
# @return [Object]
|
|
509
|
+
def extract_plugin_info(line, plugin_tags)
|
|
510
|
+
return unless (m = line.match(/^\s*#\s*@(\w+)\b/))
|
|
511
|
+
|
|
512
|
+
plugin_tags[m[1] || ''] = true
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
# Extract exception names from a `@raise` doc line.
|
|
516
|
+
#
|
|
517
|
+
# @note module_function: when included, also defines #extract_raise_types_from_line (instance visibility: private)
|
|
518
|
+
# @param [String] line a `@raise` doc line
|
|
519
|
+
# @raise [StandardError]
|
|
520
|
+
# @return [String, nil] the exception name or nil
|
|
521
|
+
# @return [Array] if StandardError or line not matched
|
|
522
|
+
def extract_raise_types_from_line(line)
|
|
523
|
+
return [] unless line.match?(/^\s*#\s*@raise\b/)
|
|
524
|
+
|
|
525
|
+
if (m = line.match(/^\s*#\s*@raise\s*\[([^\]]+)\]/))
|
|
526
|
+
parse_raise_bracket_list(m[1])
|
|
527
|
+
elsif (m = line.match(/^\s*#\s*@raise\s+([A-Z]\w*(?:::[A-Z]\w*)*)/))
|
|
528
|
+
[m[1]]
|
|
529
|
+
else
|
|
530
|
+
[]
|
|
183
531
|
end
|
|
532
|
+
rescue StandardError
|
|
533
|
+
[]
|
|
534
|
+
end
|
|
184
535
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
536
|
+
# Parse exception names from a `@raise [ExceptionA, ExceptionB]` line.
|
|
537
|
+
#
|
|
538
|
+
# @note module_function: when included, also defines #parse_raise_bracket_list (instance visibility: private)
|
|
539
|
+
# @param [Object] str comma-separated exception names string from @raise brackets
|
|
540
|
+
# @return [Array<String>, nil] the exception names or nil
|
|
541
|
+
def parse_raise_bracket_list(str)
|
|
542
|
+
str.to_s.split(',').map(&:strip).reject(&:empty?)
|
|
543
|
+
end
|
|
190
544
|
|
|
191
|
-
|
|
192
|
-
|
|
545
|
+
# Build a param name => type map from a method node.
|
|
546
|
+
#
|
|
547
|
+
# @note module_function: when included, also defines #build_param_types_from_node (instance visibility: private)
|
|
548
|
+
# @private
|
|
549
|
+
# @param [Parser::AST::Node] node def or defs node
|
|
550
|
+
# @param [Object, nil] external_sig external signature if available
|
|
551
|
+
# @param [Docscribe::Config] config
|
|
552
|
+
# @return [Hash{String => String}, nil]
|
|
553
|
+
def build_param_types_from_node(node, external_sig:, config:)
|
|
554
|
+
return unless node
|
|
555
|
+
|
|
556
|
+
args = extract_args_from_node(node)
|
|
557
|
+
return unless args
|
|
558
|
+
|
|
559
|
+
param_types = {} #: Hash[String, String]
|
|
560
|
+
collect_all_param_types(args, param_types, external_sig, config)
|
|
561
|
+
param_types.empty? ? nil : param_types
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
# Collect param types for all args using dispatch hash.
|
|
565
|
+
#
|
|
566
|
+
# @note module_function: when included, also defines #collect_all_param_types (instance visibility: private)
|
|
567
|
+
# @param [Object] args
|
|
568
|
+
# @param [Hash] param_types
|
|
569
|
+
# @param [Object] external_sig
|
|
570
|
+
# @param [Object] config
|
|
571
|
+
# @return [void]
|
|
572
|
+
def collect_all_param_types(args, param_types, external_sig, config)
|
|
573
|
+
(args.children || []).each do |a|
|
|
574
|
+
collector = PARAM_TYPE_COLLECTORS[a.type]
|
|
575
|
+
collector&.call(a, param_types, external_sig, config)
|
|
193
576
|
end
|
|
577
|
+
end
|
|
194
578
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
579
|
+
# Collect param type for a required/keyword argument.
|
|
580
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
581
|
+
# @private
|
|
582
|
+
# @param [Object] param_types hash accumulating parameter name-to-type mappings
|
|
583
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
584
|
+
# @param [Object] config Docscribe configuration for fallback type options
|
|
585
|
+
# @param [Object] infer_name lambda to transform parameter name for inference
|
|
586
|
+
# @param [Object] arg_node AST node for the required/keyword argument
|
|
587
|
+
# @return [Object]
|
|
588
|
+
def collect_param_type(arg_node, param_types, external_sig, config, infer_name:)
|
|
589
|
+
pname = arg_node.children.first.to_s
|
|
590
|
+
infer_pname = resolve_infer_name(pname, infer_name)
|
|
591
|
+
ty = external_sig&.param_types&.[](pname) ||
|
|
592
|
+
Infer.infer_param_type(infer_pname, nil,
|
|
593
|
+
fallback_type: config.fallback_type,
|
|
594
|
+
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?)
|
|
595
|
+
param_types[pname] = ty
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
# Collect param type for an optional/keyword optional argument.
|
|
599
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
600
|
+
# @private
|
|
601
|
+
# @param [Object] param_types hash accumulating parameter name-to-type mappings
|
|
602
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
603
|
+
# @param [Object] config Docscribe configuration for fallback type options
|
|
604
|
+
# @param [Object] infer_name lambda to transform parameter name for inference
|
|
605
|
+
# @param [Object] arg_node AST node for the optional/keyword optional argument
|
|
606
|
+
# @return [Object]
|
|
607
|
+
def collect_optarg_param_type(arg_node, param_types, external_sig, config, infer_name:)
|
|
608
|
+
pname, default = *arg_node
|
|
609
|
+
pname = pname.to_s
|
|
610
|
+
default_src = source_from_node(default)
|
|
611
|
+
infer_pname = resolve_infer_name(pname, infer_name)
|
|
612
|
+
ty = external_sig&.param_types&.[](pname) ||
|
|
613
|
+
Infer.infer_param_type(infer_pname, default_src,
|
|
614
|
+
fallback_type: config.fallback_type,
|
|
615
|
+
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?)
|
|
616
|
+
param_types[pname] = ty
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# Merge visibility tag lines for safe merge mode.
|
|
620
|
+
#
|
|
621
|
+
# @note module_function: when included, also defines #merge_visibility_tag_lines (instance visibility: private)
|
|
622
|
+
# @param [String] indent
|
|
623
|
+
# @param [Symbol] visibility
|
|
624
|
+
# @param [Docscribe::Config] config
|
|
625
|
+
# @param [Hash] info
|
|
626
|
+
# @return [Array<String>]
|
|
627
|
+
def merge_visibility_tag_lines(indent, visibility, config, info)
|
|
628
|
+
return [] unless config.emit_visibility_tags?
|
|
629
|
+
|
|
630
|
+
if visibility == :private && !info[:has_private]
|
|
631
|
+
["#{indent}# @private"]
|
|
632
|
+
elsif visibility == :protected && !info[:has_protected]
|
|
633
|
+
["#{indent}# @protected"]
|
|
634
|
+
else
|
|
635
|
+
[]
|
|
200
636
|
end
|
|
637
|
+
end
|
|
201
638
|
|
|
202
|
-
|
|
203
|
-
|
|
639
|
+
# Merge module_function note line for safe merge mode.
|
|
640
|
+
#
|
|
641
|
+
# @note also defines #merge_module_function_note_lines (instance: private)
|
|
642
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
643
|
+
# @param [String] indent
|
|
644
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
645
|
+
# @param [String] name
|
|
646
|
+
# @param [Hash] info
|
|
647
|
+
# @return [Array<String>]
|
|
648
|
+
def merge_module_function_note_lines(indent, insertion, name, info)
|
|
649
|
+
unless insertion.respond_to?(:module_function) && insertion.module_function && !info[:has_module_function_note]
|
|
650
|
+
return []
|
|
204
651
|
end
|
|
205
652
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
653
|
+
included_vis = insertion.included_instance_visibility || :private
|
|
654
|
+
["#{indent}# @note module_function: when included, also defines ##{name} " \
|
|
655
|
+
"(instance visibility: #{included_vis})"]
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
# Merge param lines for safe merge mode.
|
|
659
|
+
#
|
|
660
|
+
# @note module_function: when included, also defines #merge_param_lines (instance visibility: private)
|
|
661
|
+
# @param [Parser::AST::Node] node
|
|
662
|
+
# @param [String] indent
|
|
663
|
+
# @param [Docscribe::Config] config
|
|
664
|
+
# @param [Hash] opts additional options including external_sig, param_types, info
|
|
665
|
+
# @return [Array<String>]
|
|
666
|
+
def merge_param_lines(node, indent, config:, **opts)
|
|
667
|
+
return [] unless config.emit_param_tags?
|
|
668
|
+
|
|
669
|
+
all_params = build_params_lines(node, indent, external_sig: opts[:external_sig], config: config,
|
|
670
|
+
param_types_override: opts[:param_types])
|
|
671
|
+
return [] unless all_params
|
|
672
|
+
|
|
673
|
+
info = opts[:info]
|
|
674
|
+
all_params.each_with_object([]) do |pl, result|
|
|
675
|
+
pname = extract_param_name_from_param_line(pl)
|
|
676
|
+
next if pname.nil? || info[:param_names].include?(pname)
|
|
677
|
+
|
|
678
|
+
result << pl
|
|
210
679
|
end
|
|
680
|
+
end
|
|
211
681
|
|
|
212
|
-
|
|
213
|
-
|
|
682
|
+
# Merge raise tag lines for safe merge mode.
|
|
683
|
+
#
|
|
684
|
+
# @note module_function: when included, also defines #merge_raise_tag_lines (instance visibility: private)
|
|
685
|
+
# @param [Parser::AST::Node] node
|
|
686
|
+
# @param [String] indent
|
|
687
|
+
# @param [Docscribe::Config] config
|
|
688
|
+
# @param [Hash] info
|
|
689
|
+
# @return [Array<String>]
|
|
690
|
+
def merge_raise_tag_lines(node, indent, config, info)
|
|
691
|
+
return [] unless config.emit_raise_tags?
|
|
214
692
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
693
|
+
inferred = Docscribe::Infer.infer_raises_from_node(node)
|
|
694
|
+
existing = info[:raise_types] || {}
|
|
695
|
+
inferred.reject { |rt| existing[rt] }
|
|
696
|
+
.map { |rt| "#{indent}# @raise [#{rt}]" }
|
|
219
697
|
end
|
|
220
698
|
|
|
221
|
-
#
|
|
699
|
+
# Merge return tag line for safe merge mode.
|
|
222
700
|
#
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
#
|
|
701
|
+
# @note module_function: when included, also defines #merge_return_tag_line (instance visibility: private)
|
|
702
|
+
# @param [String] indent
|
|
703
|
+
# @param [String] normal_type
|
|
704
|
+
# @param [Docscribe::Config] config
|
|
705
|
+
# @param [Hash] opts additional options including scope, visibility, info
|
|
706
|
+
# @return [String, nil]
|
|
707
|
+
def merge_return_tag_line(indent, normal_type, config:, **opts)
|
|
708
|
+
return unless config.emit_return_tag?(opts[:scope], opts[:visibility])
|
|
709
|
+
return if opts[:info][:has_return]
|
|
710
|
+
|
|
711
|
+
"#{indent}# @return [#{normal_type}]"
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# Merge rescue conditional return lines for safe merge mode.
|
|
226
715
|
#
|
|
227
|
-
# @note module_function: when included, also defines #
|
|
228
|
-
# @param [
|
|
229
|
-
# @param [Array
|
|
716
|
+
# @note module_function: when included, also defines #merge_rescue_return_lines (instance visibility: private)
|
|
717
|
+
# @param [String] indent
|
|
718
|
+
# @param [Array] rescue_specs
|
|
230
719
|
# @param [Docscribe::Config] config
|
|
231
|
-
# @param [
|
|
232
|
-
# @
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
# @param [nil] override_tags Param documentation.
|
|
237
|
-
# @raise [StandardError]
|
|
238
|
-
# @return [Hash]
|
|
239
|
-
def build_missing_merge_result(insertion, existing_lines:, config:, signature_provider: nil,
|
|
240
|
-
core_rbs_provider: nil, param_types: nil, strategy: nil, return_type_override: nil, override_tags: nil)
|
|
241
|
-
node = insertion.node
|
|
242
|
-
name = SourceHelpers.node_name(node)
|
|
243
|
-
return { lines: [], reasons: [] } unless name
|
|
720
|
+
# @param [Hash] info
|
|
721
|
+
# @return [Array<String>]
|
|
722
|
+
def merge_rescue_return_lines(indent, rescue_specs, config, info)
|
|
723
|
+
return [] unless config.emit_rescue_conditional_returns?
|
|
724
|
+
return [] if info[:has_return]
|
|
244
725
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
726
|
+
rescue_specs.map do |exceptions, rtype|
|
|
727
|
+
"#{indent}# @return [#{rtype}] if #{exceptions.join(', ')}"
|
|
728
|
+
end
|
|
729
|
+
end
|
|
249
730
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
731
|
+
# Collect missing visibility tag for build_missing_merge_result.
|
|
732
|
+
#
|
|
733
|
+
# @note module_function: when included, also defines #collect_missing_visibility! (instance visibility: private)
|
|
734
|
+
# @param [Array<String>] lines
|
|
735
|
+
# @param [Array<Hash>] reasons
|
|
736
|
+
# @param [Hash] ctx
|
|
737
|
+
# @return [void]
|
|
738
|
+
def collect_missing_visibility!(lines, reasons, **ctx)
|
|
739
|
+
return unless ctx[:config].emit_visibility_tags?
|
|
255
740
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
nil_as_optional: config.nil_as_optional?,
|
|
260
|
-
param_types: param_types,
|
|
261
|
-
core_rbs_provider: core_rbs_provider
|
|
262
|
-
)
|
|
741
|
+
add_missing_private(lines, reasons, ctx)
|
|
742
|
+
add_missing_protected(lines, reasons, ctx)
|
|
743
|
+
end
|
|
263
744
|
|
|
264
|
-
|
|
265
|
-
|
|
745
|
+
# Add @private tag if missing.
|
|
746
|
+
#
|
|
747
|
+
# @note module_function: when included, also defines #add_missing_private (instance visibility: private)
|
|
748
|
+
# @param [Array] lines
|
|
749
|
+
# @param [Array] reasons
|
|
750
|
+
# @param [Hash] ctx
|
|
751
|
+
# @return [void]
|
|
752
|
+
def add_missing_private(lines, reasons, ctx)
|
|
753
|
+
return unless ctx[:visibility] == :private && !ctx[:info][:has_private]
|
|
266
754
|
|
|
267
|
-
lines
|
|
268
|
-
reasons
|
|
755
|
+
lines << "#{ctx[:indent]}# @private\n"
|
|
756
|
+
reasons << { type: :missing_visibility, message: 'missing @private' }
|
|
757
|
+
end
|
|
269
758
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
759
|
+
# Add @protected tag if missing.
|
|
760
|
+
#
|
|
761
|
+
# @note module_function: when included, also defines #add_missing_protected (instance visibility: private)
|
|
762
|
+
# @param [Array] lines
|
|
763
|
+
# @param [Array] reasons
|
|
764
|
+
# @param [Hash] ctx
|
|
765
|
+
# @return [void]
|
|
766
|
+
def add_missing_protected(lines, reasons, ctx)
|
|
767
|
+
return unless ctx[:visibility] == :protected && !ctx[:info][:has_protected]
|
|
768
|
+
|
|
769
|
+
lines << "#{ctx[:indent]}# @protected\n"
|
|
770
|
+
reasons << { type: :missing_visibility, message: 'missing @protected' }
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
# Collect missing module_function note for build_missing_merge_result.
|
|
774
|
+
#
|
|
775
|
+
# @note also defines #collect_missing_module_function_note! (instance: private)
|
|
776
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
777
|
+
# @param [Array<String>] lines
|
|
778
|
+
# @param [Array<Hash>] reasons
|
|
779
|
+
# @param [Hash] ctx
|
|
780
|
+
# @return [void]
|
|
781
|
+
def collect_missing_module_function_note!(lines, reasons, **ctx)
|
|
782
|
+
insertion = ctx[:insertion]
|
|
783
|
+
unless insertion.respond_to?(:module_function) && insertion.module_function &&
|
|
784
|
+
!ctx[:info][:has_module_function_note]
|
|
785
|
+
return
|
|
278
786
|
end
|
|
279
787
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
788
|
+
included_vis = insertion.included_instance_visibility || :private
|
|
789
|
+
lines << "#{ctx[:indent]}# @note module_function: when included, also defines ##{ctx[:name]} " \
|
|
790
|
+
"(instance visibility: #{included_vis})\n"
|
|
791
|
+
reasons << { type: :missing_module_function_note, message: 'missing module_function note' }
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
# Collect missing/updated param lines for build_missing_merge_result.
|
|
795
|
+
#
|
|
796
|
+
# @note module_function: when included, also defines #collect_missing_params! (instance visibility: private)
|
|
797
|
+
# @param [Array<String>] lines
|
|
798
|
+
# @param [Array<Hash>] reasons
|
|
799
|
+
# @param [Hash] ctx
|
|
800
|
+
# @return [void]
|
|
801
|
+
def collect_missing_params!(lines, reasons, **ctx)
|
|
802
|
+
return unless ctx[:config].emit_param_tags?
|
|
803
|
+
|
|
804
|
+
all_params = build_params_lines(ctx[:node], ctx[:indent],
|
|
805
|
+
external_sig: ctx[:external_sig], config: ctx[:config],
|
|
806
|
+
param_types_override: ctx[:param_types])
|
|
807
|
+
return unless all_params
|
|
808
|
+
|
|
809
|
+
all_params.each { |pl| collect_param_from_line(pl, lines, reasons, ctx) }
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
# Collect a single param line for build_missing_merge_result.
|
|
813
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
814
|
+
# @private
|
|
815
|
+
# @param [Object] lines array of output doc lines being accumulated
|
|
816
|
+
# @param [Object] reasons array of reason hashes for --explain output
|
|
817
|
+
# @param [Object] ctx merged context hash with build parameters
|
|
818
|
+
# @param [Object] param_line a single @param tag line to evaluate
|
|
819
|
+
# @return [Object]
|
|
820
|
+
def collect_param_from_line(param_line, lines, reasons, ctx)
|
|
821
|
+
pname = extract_param_name_from_param_line(param_line)
|
|
822
|
+
return unless pname
|
|
823
|
+
|
|
824
|
+
if !ctx[:info][:param_names].include?(pname)
|
|
825
|
+
lines << "#{param_line}\n"
|
|
826
|
+
reasons << { type: :missing_param, message: "missing @param #{pname}", extra: { param: pname } }
|
|
827
|
+
elsif ctx[:external_sig] && ctx[:info][:param_types][pname]
|
|
828
|
+
collect_updated_param(param_line, pname, lines, reasons, ctx)
|
|
285
829
|
end
|
|
830
|
+
end
|
|
286
831
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
832
|
+
# Collect an updated param line.
|
|
833
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
834
|
+
# @private
|
|
835
|
+
# @param [Object] pname the parameter name string
|
|
836
|
+
# @param [Object] lines array of output doc lines being accumulated
|
|
837
|
+
# @param [Object] reasons array of reason hashes for --explain output
|
|
838
|
+
# @param [Object] ctx merged context hash with build parameters
|
|
839
|
+
# @param [Object] param_line a single @param tag line to evaluate
|
|
840
|
+
# @return [Object]
|
|
841
|
+
def collect_updated_param(param_line, pname, lines, reasons, ctx)
|
|
842
|
+
new_type = extract_param_type_from_param_line(param_line)
|
|
843
|
+
return unless new_type && ctx[:info][:param_types][pname] != new_type
|
|
844
|
+
|
|
845
|
+
lines << "#{param_line}\n" unless ctx[:strategy] == :safe
|
|
846
|
+
reasons << {
|
|
847
|
+
type: :updated_param,
|
|
848
|
+
message: "updated @param #{pname} from #{ctx[:info][:param_types][pname]} to #{new_type}",
|
|
849
|
+
extra: { param: pname }
|
|
850
|
+
}
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
# Build generated `@param` / `@option` lines for a method node.
|
|
854
|
+
#
|
|
855
|
+
# External signatures take precedence over inferred parameter types.
|
|
856
|
+
#
|
|
857
|
+
# @note module_function: when included, also defines #build_params_lines (instance visibility: private)
|
|
858
|
+
# @param [Parser::AST::Node] node
|
|
859
|
+
# @param [String] indent
|
|
860
|
+
# @param [Docscribe::Types::MethodSignature, nil] external_sig
|
|
861
|
+
# @param [Docscribe::Config] config
|
|
862
|
+
# @param [nil] param_types_override parameter name -> type map override
|
|
863
|
+
# @return [Array<String>, nil]
|
|
864
|
+
def build_params_lines(node, indent, external_sig:, config:, param_types_override: nil)
|
|
865
|
+
args = extract_args_from_node(node)
|
|
866
|
+
return nil unless args
|
|
867
|
+
|
|
868
|
+
build_all_param_lines(args, indent, external_sig, param_types_override, config)
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
# Build all param lines for args.
|
|
872
|
+
#
|
|
873
|
+
# @note module_function: when included, also defines #build_all_param_lines (instance visibility: private)
|
|
874
|
+
# @param [Object] args
|
|
875
|
+
# @param [String] indent
|
|
876
|
+
# @param [Object] external_sig
|
|
877
|
+
# @param [Object] param_types_override
|
|
878
|
+
# @param [Docscribe::Config] config
|
|
879
|
+
# @return [Array<String>, nil]
|
|
880
|
+
def build_all_param_lines(args, indent, external_sig, param_types_override, config)
|
|
881
|
+
fb = config.fallback_type
|
|
882
|
+
tk = config.treat_options_keyword_as_hash?
|
|
883
|
+
ts = config.param_tag_style
|
|
884
|
+
pd = config.include_param_documentation? ? config.param_documentation : ''
|
|
885
|
+
params = (args.children || []).each_with_object([]) do |a, p|
|
|
886
|
+
p.concat(build_param_line(a, indent, external_sig, param_types_override,
|
|
887
|
+
fallback_type: fb, treat_options_keyword_as_hash: tk,
|
|
888
|
+
param_documentation: pd, param_tag_style: ts))
|
|
309
889
|
end
|
|
890
|
+
params.empty? ? nil : params
|
|
891
|
+
end
|
|
310
892
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
893
|
+
# Build doc lines for a full doc block.
|
|
894
|
+
# Delegates to assemble_doc_lines with setup and context.
|
|
895
|
+
#
|
|
896
|
+
# @note module_function: when included, also defines #build_doc_lines (instance visibility: private)
|
|
897
|
+
# @param [Object] setup method setup hash with indent, name, types, scope
|
|
898
|
+
# @param [Object] config Docscribe configuration object
|
|
899
|
+
# @param [Hash] kwargs additional keyword args including insertion, params_lines, raise_types, override_tags
|
|
900
|
+
# @return [Object]
|
|
901
|
+
def build_doc_lines(setup, config:, **kwargs)
|
|
902
|
+
i = setup[:indent]
|
|
903
|
+
assemble_doc_lines(i, setup, config: config, insertion: kwargs[:insertion],
|
|
904
|
+
params_lines: kwargs[:params_lines],
|
|
905
|
+
raise_types: kwargs[:raise_types], override_tags: kwargs[:override_tags])
|
|
906
|
+
end
|
|
315
907
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
908
|
+
# Assemble all doc lines into a single array.
|
|
909
|
+
#
|
|
910
|
+
# @note module_function: when included, also defines #assemble_doc_lines (instance visibility: private)
|
|
911
|
+
# @param [String] indent indent
|
|
912
|
+
# @param [Hash] setup setup
|
|
913
|
+
# @param [Hash] ctx context hash with config, insertion, params_lines, raise_types, override_tags
|
|
914
|
+
# @return [Array<String>]
|
|
915
|
+
def assemble_doc_lines(indent, setup, **ctx)
|
|
916
|
+
line_ary = build_header_lines(
|
|
917
|
+
indent,
|
|
918
|
+
config: ctx[:config],
|
|
919
|
+
container: setup[:container], method_symbol: setup[:method_symbol], name: setup[:name],
|
|
920
|
+
normal_type: setup[:normal_type]
|
|
921
|
+
)
|
|
922
|
+
|
|
923
|
+
append_assemble_body_lines(line_ary, indent, setup, ctx)
|
|
924
|
+
line_ary
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
# Append body lines to a doc line array.
|
|
928
|
+
#
|
|
929
|
+
# @note module_function: when included, also defines #append_assemble_body_lines (instance visibility: private)
|
|
930
|
+
# @param [Array<String>] line_ary
|
|
931
|
+
# @param [Hash] ctx
|
|
932
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
933
|
+
# @param [Object] setup method setup hash with name, types, scope
|
|
934
|
+
# @return [void]
|
|
935
|
+
def append_assemble_body_lines(line_ary, indent, setup, ctx)
|
|
936
|
+
line_ary.concat(build_all_body_tags(indent, setup, ctx))
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
# Build all body tag lines for a doc block.
|
|
940
|
+
#
|
|
941
|
+
# @note module_function: when included, also defines #build_all_body_tags (instance visibility: private)
|
|
942
|
+
# @param [Hash] ctx
|
|
943
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
944
|
+
# @param [Object] setup method setup hash with name, types, scope
|
|
945
|
+
# @return [Array<String>]
|
|
946
|
+
def build_all_body_tags(indent, setup, ctx)
|
|
947
|
+
result = core_body_tags(indent, setup, ctx)
|
|
948
|
+
result.insert(3, ctx[:params_lines]) if ctx[:params_lines]
|
|
949
|
+
result.flatten
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
# Core body tags without optional params_lines.
|
|
953
|
+
#
|
|
954
|
+
# @note module_function: when included, also defines #core_body_tags (instance visibility: private)
|
|
955
|
+
# @param [Hash] ctx
|
|
956
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
957
|
+
# @param [Object] setup method setup hash with name, types, scope
|
|
958
|
+
# @return [Array]
|
|
959
|
+
def core_body_tags(indent, setup, ctx)
|
|
960
|
+
config = ctx[:config]
|
|
961
|
+
[
|
|
962
|
+
defaults_and_visibility(indent, config, setup[:scope], setup[:visibility]),
|
|
963
|
+
build_module_function_note_lines(indent, ctx[:insertion], setup[:name]),
|
|
964
|
+
build_raise_tag_lines(indent, ctx[:raise_types], config),
|
|
965
|
+
build_return_line_if_needed(indent, setup, config),
|
|
966
|
+
build_rescue_return_lines(indent, setup[:rescue_specs], config),
|
|
967
|
+
build_plugin_tag_lines(ctx[:insertion], indent, setup[:normal_type], ctx[:override_tags])
|
|
968
|
+
]
|
|
969
|
+
end
|
|
970
|
+
|
|
971
|
+
# Build default msg and visibility tags.
|
|
972
|
+
#
|
|
973
|
+
# @note module_function: when included, also defines #defaults_and_visibility (instance visibility: private)
|
|
974
|
+
# @param [Object] config
|
|
975
|
+
# @param [Symbol] scope
|
|
976
|
+
# @param [Symbol] visibility
|
|
977
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
978
|
+
# @return [Array<String>]
|
|
979
|
+
def defaults_and_visibility(indent, config, scope, visibility)
|
|
980
|
+
[
|
|
981
|
+
build_default_msg_lines(indent, config, scope, visibility),
|
|
982
|
+
build_visibility_tag_lines(indent, visibility, config)
|
|
983
|
+
].flatten
|
|
984
|
+
end
|
|
985
|
+
|
|
986
|
+
# Build return tag line if emit condition is met.
|
|
987
|
+
#
|
|
988
|
+
# @note module_function: when included, also defines #build_return_line_if_needed (instance visibility: private)
|
|
989
|
+
# @param [Docscribe::Config] config
|
|
990
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
991
|
+
# @param [Object] setup method setup hash with name, normal_type, scope, visibility
|
|
992
|
+
# @return [Array<String>]
|
|
993
|
+
def build_return_line_if_needed(indent, setup, config)
|
|
994
|
+
emit_ret = config.emit_return_tag?(setup[:scope], setup[:visibility])
|
|
995
|
+
ret_line = build_return_tag_line(indent, setup[:normal_type], config, setup[:scope], setup[:visibility])
|
|
996
|
+
emit_ret && ret_line ? [ret_line] : []
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
# Extract args sub-node from a def or defs node.
|
|
1000
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1001
|
+
# @private
|
|
1002
|
+
# @param [Parser::AST::Node] node
|
|
1003
|
+
# @return [Parser::AST::Node, nil]
|
|
1004
|
+
def extract_args_from_node(node)
|
|
1005
|
+
case node.type
|
|
1006
|
+
when :def then node.children[1]
|
|
1007
|
+
when :defs then node.children[2]
|
|
320
1008
|
end
|
|
1009
|
+
end
|
|
321
1010
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
1011
|
+
# Build a param line for a single argument node.
|
|
1012
|
+
# Dispatches to the appropriate builder via PARAM_BUILDERS by arg type.
|
|
1013
|
+
#
|
|
1014
|
+
# @note module_function: when included, also defines #build_param_line (instance visibility: private)
|
|
1015
|
+
# @param [Object] arg_node AST node for the argument
|
|
1016
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
1017
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1018
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1019
|
+
# @param [Hash] opts additional options for param formatting (fallback_type, param_tag_style, etc.)
|
|
1020
|
+
# @return [Object]
|
|
1021
|
+
def build_param_line(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1022
|
+
PARAM_BUILDERS.fetch(arg_node.type, lambda { |*|
|
|
1023
|
+
[] #: Array[String]
|
|
1024
|
+
}).call(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1025
|
+
end
|
|
1026
|
+
|
|
1027
|
+
# Build header line(s) for a doc block.
|
|
1028
|
+
#
|
|
1029
|
+
# @note module_function: when included, also defines #build_header_lines (instance visibility: private)
|
|
1030
|
+
# @param [String] indent
|
|
1031
|
+
# @param [Docscribe::Config] config
|
|
1032
|
+
# @param [Hash] opts additional options including container, method_symbol, name, normal_type
|
|
1033
|
+
# @return [Array<String>]
|
|
1034
|
+
def build_header_lines(indent, config:, **opts)
|
|
1035
|
+
if config.emit_header?
|
|
1036
|
+
c = opts[:container]
|
|
1037
|
+
ms = opts[:method_symbol]
|
|
1038
|
+
n = opts[:name]
|
|
1039
|
+
nt = opts[:normal_type]
|
|
1040
|
+
["#{indent}# +#{c}#{ms}#{n}+ -> #{nt}", "#{indent}#"]
|
|
1041
|
+
else
|
|
1042
|
+
[]
|
|
333
1043
|
end
|
|
1044
|
+
end
|
|
334
1045
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
1046
|
+
# Build default message lines for a doc block.
|
|
1047
|
+
#
|
|
1048
|
+
# @note module_function: when included, also defines #build_default_msg_lines (instance visibility: private)
|
|
1049
|
+
# @param [String] indent
|
|
1050
|
+
# @param [Docscribe::Config] config
|
|
1051
|
+
# @param [Symbol] scope
|
|
1052
|
+
# @param [Symbol] visibility
|
|
1053
|
+
# @return [Array<String>]
|
|
1054
|
+
def build_default_msg_lines(indent, config, scope, visibility)
|
|
1055
|
+
if config.include_default_message?
|
|
1056
|
+
["#{indent}# #{config.default_message(scope, visibility)}", "#{indent}#"]
|
|
1057
|
+
else
|
|
1058
|
+
[]
|
|
343
1059
|
end
|
|
344
|
-
|
|
345
|
-
plugin_tags.concat(Array(override_tags)) if override_tags
|
|
1060
|
+
end
|
|
346
1061
|
|
|
347
|
-
|
|
348
|
-
|
|
1062
|
+
# Build visibility tag line(s) for a full doc block.
|
|
1063
|
+
#
|
|
1064
|
+
# @note module_function: when included, also defines #build_visibility_tag_lines (instance visibility: private)
|
|
1065
|
+
# @param [String] indent
|
|
1066
|
+
# @param [Symbol] visibility
|
|
1067
|
+
# @param [Docscribe::Config] config
|
|
1068
|
+
# @return [Array<String>]
|
|
1069
|
+
def build_visibility_tag_lines(indent, visibility, config)
|
|
1070
|
+
return [] unless config.emit_visibility_tags?
|
|
349
1071
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
1072
|
+
case visibility
|
|
1073
|
+
when :private then ["#{indent}# @private"]
|
|
1074
|
+
when :protected then ["#{indent}# @protected"]
|
|
1075
|
+
else []
|
|
353
1076
|
end
|
|
354
|
-
{ lines: lines, reasons: reasons }
|
|
355
|
-
rescue StandardError => e
|
|
356
|
-
debug_warn(e, insertion: insertion, name: name || '(unknown)', phase: 'DocBuilder.build_missing_merge_result')
|
|
357
|
-
{ lines: [], reasons: [] }
|
|
358
1077
|
end
|
|
359
1078
|
|
|
360
|
-
#
|
|
361
|
-
#
|
|
362
|
-
# Extracts: `@param` names, `@return`, `@raise`, `@private`, `@protected`,
|
|
363
|
-
# `@module_function` notes, and `@option` lines.
|
|
1079
|
+
# Build module_function note line(s) for a full doc block.
|
|
364
1080
|
#
|
|
365
|
-
# @note
|
|
366
|
-
# @
|
|
367
|
-
# @
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
Array(lines).each do |line|
|
|
380
|
-
if (m = line.match(/^\s*#\s*@(\w+)\b/))
|
|
381
|
-
plugin_tags[m[1]] = true
|
|
382
|
-
end
|
|
383
|
-
if (pname = extract_param_name_from_param_line(line))
|
|
384
|
-
param_names[pname] = true
|
|
385
|
-
if (type_match = line.match(/@param\s+\[([^\]]+)\]\s+\S+/) || line.match(/@param\s+\S+\s+\[([^\]]+)\]/))
|
|
386
|
-
param_types[pname] = type_match[1]
|
|
387
|
-
end
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
if line.match?(/^\s*#\s*@return\b/)
|
|
391
|
-
has_return = true
|
|
392
|
-
if (m = line.match(/@return\s+\[([^\]]+)\]/))
|
|
393
|
-
return_type = m[1]
|
|
394
|
-
end
|
|
1081
|
+
# @note also defines #build_module_function_note_lines (instance: private)
|
|
1082
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1083
|
+
# @param [String] indent
|
|
1084
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
1085
|
+
# @param [String] name
|
|
1086
|
+
# @return [Array<String>]
|
|
1087
|
+
def build_module_function_note_lines(indent, insertion, name)
|
|
1088
|
+
return [] unless insertion.respond_to?(:module_function) && insertion.module_function
|
|
1089
|
+
|
|
1090
|
+
included_vis =
|
|
1091
|
+
if insertion.respond_to?(:included_instance_visibility) && insertion.included_instance_visibility
|
|
1092
|
+
insertion.included_instance_visibility
|
|
1093
|
+
else
|
|
1094
|
+
:private
|
|
395
1095
|
end
|
|
396
|
-
has_private ||= line.match?(/^\s*#\s*@private\b/)
|
|
397
|
-
has_protected ||= line.match?(/^\s*#\s*@protected\b/)
|
|
398
|
-
has_module_function_note ||= line.match?(/^\s*#\s*@note\s+module_function:/)
|
|
399
|
-
|
|
400
|
-
extract_raise_types_from_line(line).each { |t| raise_types[t] = true }
|
|
401
|
-
end
|
|
402
1096
|
|
|
403
|
-
{
|
|
404
|
-
|
|
405
|
-
param_types: param_types,
|
|
406
|
-
has_return: has_return,
|
|
407
|
-
return_type: return_type,
|
|
408
|
-
raise_types: raise_types,
|
|
409
|
-
has_private: has_private,
|
|
410
|
-
has_protected: has_protected,
|
|
411
|
-
has_module_function_note: has_module_function_note,
|
|
412
|
-
plugin_tags: plugin_tags
|
|
413
|
-
}
|
|
1097
|
+
["#{indent}# @note module_function: when included, also defines ##{name} " \
|
|
1098
|
+
"(instance visibility: #{included_vis})"]
|
|
414
1099
|
end
|
|
415
1100
|
|
|
416
|
-
#
|
|
1101
|
+
# Build raise tag lines for a full doc block.
|
|
417
1102
|
#
|
|
418
|
-
# @note module_function: when included, also defines #
|
|
419
|
-
# @param [String]
|
|
420
|
-
# @
|
|
421
|
-
# @
|
|
422
|
-
# @return [Array]
|
|
423
|
-
def
|
|
424
|
-
return [] unless
|
|
1103
|
+
# @note module_function: when included, also defines #build_raise_tag_lines (instance visibility: private)
|
|
1104
|
+
# @param [String] indent
|
|
1105
|
+
# @param [Array<String>] raise_types
|
|
1106
|
+
# @param [Docscribe::Config] config
|
|
1107
|
+
# @return [Array<String>]
|
|
1108
|
+
def build_raise_tag_lines(indent, raise_types, config)
|
|
1109
|
+
return [] unless config.emit_raise_tags?
|
|
425
1110
|
|
|
426
|
-
|
|
427
|
-
parse_raise_bracket_list(m[1])
|
|
428
|
-
elsif (m = line.match(/^\s*#\s*@raise\s+([A-Z]\w*(?:::[A-Z]\w*)*)/))
|
|
429
|
-
[m[1]]
|
|
430
|
-
else
|
|
431
|
-
[]
|
|
432
|
-
end
|
|
433
|
-
rescue StandardError
|
|
434
|
-
[]
|
|
1111
|
+
raise_types.map { |rt| "#{indent}# @raise [#{rt}]" }
|
|
435
1112
|
end
|
|
436
1113
|
|
|
437
|
-
#
|
|
1114
|
+
# Build return tag line for a full doc block.
|
|
438
1115
|
#
|
|
439
|
-
# @note module_function: when included, also defines #
|
|
440
|
-
# @param [String]
|
|
441
|
-
# @
|
|
442
|
-
|
|
443
|
-
|
|
1116
|
+
# @note module_function: when included, also defines #build_return_tag_line (instance visibility: private)
|
|
1117
|
+
# @param [String] indent
|
|
1118
|
+
# @param [String] normal_type
|
|
1119
|
+
# @param [Docscribe::Config] config
|
|
1120
|
+
# @param [Symbol] scope
|
|
1121
|
+
# @param [Symbol] visibility
|
|
1122
|
+
# @return [String, nil]
|
|
1123
|
+
def build_return_tag_line(indent, normal_type, config, scope, visibility)
|
|
1124
|
+
return unless config.emit_return_tag?(scope, visibility)
|
|
1125
|
+
|
|
1126
|
+
"#{indent}# @return [#{normal_type}]"
|
|
444
1127
|
end
|
|
445
1128
|
|
|
446
|
-
# Build
|
|
1129
|
+
# Build rescue conditional return lines for a full doc block.
|
|
447
1130
|
#
|
|
448
|
-
# @note module_function: when included, also defines #
|
|
449
|
-
# @
|
|
450
|
-
# @param [
|
|
451
|
-
# @param [Object, nil] external_sig external signature if available
|
|
1131
|
+
# @note module_function: when included, also defines #build_rescue_return_lines (instance visibility: private)
|
|
1132
|
+
# @param [String] indent
|
|
1133
|
+
# @param [Array] rescue_specs
|
|
452
1134
|
# @param [Docscribe::Config] config
|
|
453
|
-
# @return [
|
|
454
|
-
def
|
|
455
|
-
return
|
|
1135
|
+
# @return [Array<String>]
|
|
1136
|
+
def build_rescue_return_lines(indent, rescue_specs, config)
|
|
1137
|
+
return [] unless config.emit_rescue_conditional_returns?
|
|
456
1138
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
end
|
|
1139
|
+
rescue_specs.map do |exceptions, rtype|
|
|
1140
|
+
"#{indent}# @return [#{rtype}] if #{exceptions.join(', ')}"
|
|
1141
|
+
end
|
|
1142
|
+
end
|
|
462
1143
|
|
|
463
|
-
|
|
1144
|
+
# Build plugin tag lines for a full doc block.
|
|
1145
|
+
#
|
|
1146
|
+
# @note module_function: when included, also defines #build_plugin_tag_lines (instance visibility: private)
|
|
1147
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
1148
|
+
# @param [String] indent
|
|
1149
|
+
# @param [String] normal_type
|
|
1150
|
+
# @param [Array, nil] override_tags
|
|
1151
|
+
# @return [Array<String>]
|
|
1152
|
+
def build_plugin_tag_lines(insertion, indent, normal_type, override_tags)
|
|
1153
|
+
plugin_tags = Docscribe::Plugin.run_tag_plugins(build_plugin_context(insertion, normal_type: normal_type))
|
|
1154
|
+
plugin_tags.concat(Array(override_tags)) if override_tags
|
|
1155
|
+
render_plugin_tags(plugin_tags, indent)
|
|
1156
|
+
end
|
|
464
1157
|
|
|
465
|
-
|
|
1158
|
+
# Build a param line for a required argument.
|
|
1159
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1160
|
+
# @private
|
|
1161
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
1162
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1163
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1164
|
+
# @param [Object] arg_node AST node for the required argument
|
|
1165
|
+
# @param [Hash] opts additional options for param formatting
|
|
1166
|
+
# @return [Object]
|
|
1167
|
+
def build_arg_line(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1168
|
+
pname = arg_node.children.first.to_s
|
|
1169
|
+
ty = lookup_param_type(external_sig, param_types_override, pname, pname,
|
|
1170
|
+
infer_default: nil,
|
|
1171
|
+
fallback_type: opts[:fallback_type],
|
|
1172
|
+
treat_options_keyword_as_hash: opts[:treat_options_keyword_as_hash])
|
|
1173
|
+
format_param_tag(indent, pname, ty, opts[:param_documentation], style: opts[:param_tag_style])
|
|
1174
|
+
end
|
|
466
1175
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
Infer.infer_param_type(
|
|
486
|
-
pname,
|
|
487
|
-
default_src,
|
|
488
|
-
fallback_type: config.fallback_type,
|
|
489
|
-
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
490
|
-
)
|
|
491
|
-
param_types[pname] = ty
|
|
492
|
-
|
|
493
|
-
when :kwarg
|
|
494
|
-
pname = a.children.first.to_s
|
|
495
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
496
|
-
Infer.infer_param_type(
|
|
497
|
-
"#{pname}:",
|
|
498
|
-
nil,
|
|
499
|
-
fallback_type: config.fallback_type,
|
|
500
|
-
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
501
|
-
)
|
|
502
|
-
param_types[pname] = ty
|
|
503
|
-
|
|
504
|
-
when :kwoptarg
|
|
505
|
-
pname, default = *a
|
|
506
|
-
pname = pname.to_s
|
|
507
|
-
default_src = default&.loc&.expression&.source
|
|
508
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
509
|
-
Infer.infer_param_type(
|
|
510
|
-
"#{pname}:",
|
|
511
|
-
default_src,
|
|
512
|
-
fallback_type: config.fallback_type,
|
|
513
|
-
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
514
|
-
)
|
|
515
|
-
param_types[pname] = ty
|
|
516
|
-
end
|
|
517
|
-
end
|
|
1176
|
+
# Build param lines for an optional argument (including @option lines).
|
|
1177
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1178
|
+
# @private
|
|
1179
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
1180
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1181
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1182
|
+
# @param [Object] arg_node AST node for the optional argument
|
|
1183
|
+
# @param [Hash] opts additional options for param formatting
|
|
1184
|
+
# @return [Object]
|
|
1185
|
+
def build_optarg_lines(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1186
|
+
pname, default = *arg_node
|
|
1187
|
+
pname = pname.to_s
|
|
1188
|
+
ty = optarg_type(pname, default, external_sig, param_types_override, opts)
|
|
1189
|
+
lines = [format_param_tag(indent, pname, ty, opts[:param_documentation], style: opts[:param_tag_style])]
|
|
1190
|
+
|
|
1191
|
+
append_option_lines(lines, default, indent, pname, opts[:fallback_type])
|
|
1192
|
+
lines
|
|
1193
|
+
end
|
|
518
1194
|
|
|
519
|
-
|
|
1195
|
+
# Resolve optarg type.
|
|
1196
|
+
#
|
|
1197
|
+
# @note module_function: when included, also defines #optarg_type (instance visibility: private)
|
|
1198
|
+
# @param [String] pname
|
|
1199
|
+
# @param [Object] default
|
|
1200
|
+
# @param [Object] external_sig
|
|
1201
|
+
# @param [Object] param_types_override
|
|
1202
|
+
# @param [Hash] opts
|
|
1203
|
+
# @return [String]
|
|
1204
|
+
def optarg_type(pname, default, external_sig, param_types_override, opts)
|
|
1205
|
+
default_src = source_from_node(default)
|
|
1206
|
+
lookup_param_type(external_sig, param_types_override, pname, pname,
|
|
1207
|
+
infer_default: default_src,
|
|
1208
|
+
fallback_type: opts[:fallback_type],
|
|
1209
|
+
treat_options_keyword_as_hash: opts[:treat_options_keyword_as_hash])
|
|
520
1210
|
end
|
|
521
1211
|
|
|
522
|
-
#
|
|
1212
|
+
# Extract source text from an AST node.
|
|
523
1213
|
#
|
|
524
|
-
#
|
|
1214
|
+
# @note module_function: when included, also defines #source_from_node (instance visibility: private)
|
|
1215
|
+
# @param [Object] node
|
|
1216
|
+
# @return [String, nil]
|
|
1217
|
+
def source_from_node(node)
|
|
1218
|
+
loc = node&.loc
|
|
1219
|
+
loc&.expression&.source
|
|
1220
|
+
end
|
|
1221
|
+
|
|
1222
|
+
# Resolve the infer name string from a param name and infer_name lambda.
|
|
525
1223
|
#
|
|
526
|
-
# @note module_function: when included, also defines #
|
|
527
|
-
# @param [
|
|
528
|
-
# @param [
|
|
529
|
-
# @
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
def build_params_lines(node, indent, external_sig:, config:, param_types_override: nil)
|
|
534
|
-
fallback_type = config.fallback_type
|
|
535
|
-
treat_options_keyword_as_hash = config.treat_options_keyword_as_hash?
|
|
536
|
-
param_tag_style = config.param_tag_style
|
|
537
|
-
param_documentation = config.include_param_documentation? ? config.param_documentation : ''
|
|
538
|
-
|
|
539
|
-
args =
|
|
540
|
-
case node.type
|
|
541
|
-
when :def then node.children[1]
|
|
542
|
-
when :defs then node.children[2]
|
|
543
|
-
end
|
|
1224
|
+
# @note module_function: when included, also defines #resolve_infer_name (instance visibility: private)
|
|
1225
|
+
# @param [String] pname
|
|
1226
|
+
# @param [Proc, nil] infer_name
|
|
1227
|
+
# @return [String]
|
|
1228
|
+
def resolve_infer_name(pname, infer_name)
|
|
1229
|
+
infer_name ? infer_name.call(pname) : pname
|
|
1230
|
+
end
|
|
544
1231
|
|
|
545
|
-
|
|
1232
|
+
# Build a param line for a keyword argument.
|
|
1233
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1234
|
+
# @private
|
|
1235
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
1236
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1237
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1238
|
+
# @param [Object] arg_node AST node for the keyword argument
|
|
1239
|
+
# @param [Hash] opts additional options for param formatting
|
|
1240
|
+
# @return [Object]
|
|
1241
|
+
def build_kwarg_line(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1242
|
+
pname = arg_node.children.first.to_s
|
|
1243
|
+
ty = lookup_param_type(external_sig, param_types_override, pname, "#{pname}:",
|
|
1244
|
+
infer_default: nil,
|
|
1245
|
+
fallback_type: opts[:fallback_type],
|
|
1246
|
+
treat_options_keyword_as_hash: opts[:treat_options_keyword_as_hash])
|
|
1247
|
+
format_param_tag(indent, pname, ty, opts[:param_documentation], style: opts[:param_tag_style])
|
|
1248
|
+
end
|
|
546
1249
|
|
|
547
|
-
|
|
1250
|
+
# Build a param line for an optional keyword argument.
|
|
1251
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1252
|
+
# @private
|
|
1253
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
1254
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1255
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1256
|
+
# @param [Object] arg_node AST node for the optional keyword argument
|
|
1257
|
+
# @param [Hash] opts additional options for param formatting
|
|
1258
|
+
# @return [Object]
|
|
1259
|
+
def build_kwoptarg_line(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1260
|
+
pname, default = *arg_node
|
|
1261
|
+
pname = pname.to_s
|
|
1262
|
+
default_loc = default&.loc
|
|
1263
|
+
default_src = default_loc&.expression&.source
|
|
1264
|
+
ty = lookup_param_type(external_sig, param_types_override, pname, "#{pname}:",
|
|
1265
|
+
infer_default: default_src,
|
|
1266
|
+
fallback_type: opts[:fallback_type],
|
|
1267
|
+
treat_options_keyword_as_hash: opts[:treat_options_keyword_as_hash])
|
|
1268
|
+
format_param_tag(indent, pname, ty, opts[:param_documentation], style: opts[:param_tag_style])
|
|
1269
|
+
end
|
|
548
1270
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
override_param_type_for(pname, param_types_override) ||
|
|
569
|
-
Infer.infer_param_type(
|
|
570
|
-
pname,
|
|
571
|
-
default_src,
|
|
572
|
-
fallback_type: fallback_type,
|
|
573
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
574
|
-
)
|
|
575
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
576
|
-
|
|
577
|
-
hash_option_pairs(default).each do |pair|
|
|
578
|
-
key_node, value_node = pair.children
|
|
579
|
-
option_key = option_key_name(key_node)
|
|
580
|
-
option_type = Infer::Literals.type_from_literal(value_node, fallback_type: fallback_type)
|
|
581
|
-
option_default = node_default_literal(value_node)
|
|
582
|
-
|
|
583
|
-
line = "#{indent}# @option #{pname} [#{option_type}] :#{option_key}"
|
|
584
|
-
line += " (#{option_default})" if option_default
|
|
585
|
-
line += ' Option documentation.'
|
|
586
|
-
params << line
|
|
587
|
-
end
|
|
588
|
-
|
|
589
|
-
when :kwarg
|
|
590
|
-
pname = a.children.first.to_s
|
|
591
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
592
|
-
override_param_type_for(pname, param_types_override) ||
|
|
593
|
-
Infer.infer_param_type(
|
|
594
|
-
"#{pname}:",
|
|
595
|
-
nil,
|
|
596
|
-
fallback_type: fallback_type,
|
|
597
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
598
|
-
)
|
|
599
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
600
|
-
|
|
601
|
-
when :kwoptarg
|
|
602
|
-
pname, default = *a
|
|
603
|
-
pname = pname.to_s
|
|
604
|
-
default_src = default&.loc&.expression&.source
|
|
605
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
606
|
-
override_param_type_for(pname, param_types_override) ||
|
|
607
|
-
Infer.infer_param_type(
|
|
608
|
-
"#{pname}:",
|
|
609
|
-
default_src,
|
|
610
|
-
fallback_type: fallback_type,
|
|
611
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
612
|
-
)
|
|
613
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
614
|
-
|
|
615
|
-
when :restarg
|
|
616
|
-
pname = (a.children.first || 'args').to_s
|
|
617
|
-
ty =
|
|
618
|
-
if external_sig&.rest_positional&.element_type
|
|
619
|
-
"Array<#{external_sig.rest_positional.element_type}>"
|
|
620
|
-
else
|
|
621
|
-
override_param_type_for(pname, param_types_override) ||
|
|
622
|
-
Infer.infer_param_type(
|
|
623
|
-
"*#{pname}",
|
|
624
|
-
nil,
|
|
625
|
-
fallback_type: fallback_type,
|
|
626
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
627
|
-
)
|
|
628
|
-
end
|
|
629
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
630
|
-
|
|
631
|
-
when :kwrestarg
|
|
632
|
-
pname = (a.children.first || 'kwargs').to_s
|
|
633
|
-
ty = external_sig&.rest_keywords&.type ||
|
|
634
|
-
override_param_type_for(pname, param_types_override) ||
|
|
635
|
-
Infer.infer_param_type(
|
|
636
|
-
"**#{pname}",
|
|
637
|
-
nil,
|
|
638
|
-
fallback_type: fallback_type,
|
|
639
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
640
|
-
)
|
|
641
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
642
|
-
|
|
643
|
-
when :blockarg
|
|
644
|
-
pname = (a.children.first || 'block').to_s
|
|
645
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
646
|
-
override_param_type_for(pname, param_types_override) ||
|
|
647
|
-
Infer.infer_param_type(
|
|
648
|
-
"&#{pname}",
|
|
649
|
-
nil,
|
|
650
|
-
fallback_type: fallback_type,
|
|
651
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
652
|
-
)
|
|
653
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
654
|
-
|
|
655
|
-
when :forward_arg
|
|
656
|
-
# skip
|
|
657
|
-
end
|
|
658
|
-
end
|
|
1271
|
+
# Build a param line for a rest argument (*args).
|
|
1272
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1273
|
+
# @private
|
|
1274
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
1275
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1276
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1277
|
+
# @param [Object] arg_node AST node for the rest argument (*args)
|
|
1278
|
+
# @param [Hash] opts additional options for param formatting
|
|
1279
|
+
# @return [Object]
|
|
1280
|
+
def build_restarg_line(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1281
|
+
pname = (arg_node.children.first || 'args').to_s
|
|
1282
|
+
ty = if external_sig&.rest_positional&.element_type
|
|
1283
|
+
"Array<#{external_sig.rest_positional.element_type}>"
|
|
1284
|
+
else
|
|
1285
|
+
lookup_param_type_by_infer(param_types_override, pname, "*#{pname}",
|
|
1286
|
+
opts[:fallback_type], opts[:treat_options_keyword_as_hash])
|
|
1287
|
+
end
|
|
1288
|
+
format_param_tag(indent, pname, ty, opts[:param_documentation], style: opts[:param_tag_style])
|
|
1289
|
+
end
|
|
659
1290
|
|
|
660
|
-
|
|
1291
|
+
# Build a param line for a keyword rest argument (**kwargs).
|
|
1292
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1293
|
+
# @private
|
|
1294
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
1295
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1296
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1297
|
+
# @param [Object] arg_node AST node for the keyword rest argument (**kwargs)
|
|
1298
|
+
# @param [Hash] opts additional options for param formatting
|
|
1299
|
+
# @return [Object]
|
|
1300
|
+
def build_kwrestarg_line(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1301
|
+
pname = (arg_node.children.first || 'kwargs').to_s
|
|
1302
|
+
ty = external_sig&.rest_keywords&.type ||
|
|
1303
|
+
lookup_param_type_by_infer(param_types_override, pname, "**#{pname}",
|
|
1304
|
+
opts[:fallback_type], opts[:treat_options_keyword_as_hash])
|
|
1305
|
+
format_param_tag(indent, pname, ty, opts[:param_documentation], style: opts[:param_tag_style])
|
|
661
1306
|
end
|
|
662
1307
|
|
|
663
|
-
#
|
|
664
|
-
#
|
|
665
|
-
# @
|
|
666
|
-
# @param [Object]
|
|
667
|
-
# @param [Object]
|
|
1308
|
+
# Build a param line for a block argument (&block).
|
|
1309
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1310
|
+
# @private
|
|
1311
|
+
# @param [Object] indent indentation string for doc comment lines
|
|
1312
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1313
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1314
|
+
# @param [Object] arg_node AST node for the block argument (&block)
|
|
1315
|
+
# @param [Hash] opts additional options for param formatting
|
|
668
1316
|
# @return [Object]
|
|
669
|
-
def
|
|
670
|
-
|
|
1317
|
+
def build_blockarg_line(arg_node, indent, external_sig, param_types_override, **opts)
|
|
1318
|
+
pname = (arg_node.children.first || 'block').to_s
|
|
1319
|
+
ty = lookup_param_type(external_sig, param_types_override, pname, "&#{pname}",
|
|
1320
|
+
infer_default: nil,
|
|
1321
|
+
fallback_type: opts[:fallback_type],
|
|
1322
|
+
treat_options_keyword_as_hash: opts[:treat_options_keyword_as_hash])
|
|
1323
|
+
format_param_tag(indent, pname, ty, opts[:param_documentation], style: opts[:param_tag_style])
|
|
1324
|
+
end
|
|
671
1325
|
|
|
672
|
-
|
|
673
|
-
|
|
1326
|
+
# Three-tier type lookup: external_sig -> override -> inference.
|
|
1327
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1328
|
+
# @private
|
|
1329
|
+
# @param [Object] external_sig external method signature for type overrides
|
|
1330
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1331
|
+
# @param [Object] pname the parameter name string
|
|
1332
|
+
# @param [Object] infer_name parameter name string or transformed version for inference
|
|
1333
|
+
# @param [Hash] opts additional options including infer_default, fallback_type, treat_options_keyword_as_hash
|
|
1334
|
+
# @return [Object]
|
|
1335
|
+
def lookup_param_type(external_sig, param_types_override, pname, infer_name, **opts)
|
|
1336
|
+
external_sig&.param_types&.[](pname) ||
|
|
1337
|
+
override_param_type_for(pname, param_types_override) ||
|
|
1338
|
+
Infer.infer_param_type(infer_name, opts[:infer_default],
|
|
1339
|
+
fallback_type: opts[:fallback_type],
|
|
1340
|
+
treat_options_keyword_as_hash: opts[:treat_options_keyword_as_hash])
|
|
1341
|
+
end
|
|
1342
|
+
|
|
1343
|
+
# Two-tier type lookup: override -> inference (for rest/kwrest types).
|
|
1344
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1345
|
+
# @private
|
|
1346
|
+
# @param [Object] param_types_override map of parameter name to override type
|
|
1347
|
+
# @param [Object] pname the parameter name string
|
|
1348
|
+
# @param [Object] infer_name parameter name string or transformed version for inference
|
|
1349
|
+
# @param [Object] fallback_type default type string when inference fails
|
|
1350
|
+
# @param [Object] treat_options_keyword_as_hash whether to treat options keyword as Hash type
|
|
1351
|
+
# @return [Object]
|
|
1352
|
+
def lookup_param_type_by_infer(param_types_override, pname, infer_name, fallback_type,
|
|
1353
|
+
treat_options_keyword_as_hash)
|
|
1354
|
+
override_param_type_for(pname, param_types_override) ||
|
|
1355
|
+
Infer.infer_param_type(infer_name, nil,
|
|
1356
|
+
fallback_type: fallback_type,
|
|
1357
|
+
treat_options_keyword_as_hash: treat_options_keyword_as_hash || false)
|
|
674
1358
|
end
|
|
675
1359
|
|
|
676
|
-
# Format a
|
|
1360
|
+
# Format a YARD @param tag line with optional documentation text.
|
|
677
1361
|
#
|
|
678
1362
|
# @note module_function: when included, also defines #format_param_tag (instance visibility: private)
|
|
679
|
-
# @param [
|
|
680
|
-
# @param [
|
|
681
|
-
# @param [
|
|
682
|
-
# @param [
|
|
683
|
-
# @param [
|
|
684
|
-
# @return [
|
|
1363
|
+
# @param [Object] indent indentation string for the doc line
|
|
1364
|
+
# @param [Object] name the parameter name
|
|
1365
|
+
# @param [Object] type the parameter type string
|
|
1366
|
+
# @param [Object] documentation optional documentation text appended to the tag
|
|
1367
|
+
# @param [Object] style param tag style (:type_name or :name_type)
|
|
1368
|
+
# @return [Object]
|
|
685
1369
|
def format_param_tag(indent, name, type, documentation, style:)
|
|
686
1370
|
doc = documentation.to_s.strip
|
|
687
1371
|
type = type.to_s
|
|
@@ -696,45 +1380,96 @@ module Docscribe
|
|
|
696
1380
|
doc.empty? ? line : "#{line} #{doc}"
|
|
697
1381
|
end
|
|
698
1382
|
|
|
699
|
-
#
|
|
1383
|
+
# Append @option lines for hash defaults.
|
|
1384
|
+
#
|
|
1385
|
+
# @note module_function: when included, also defines #append_option_lines (instance visibility: private)
|
|
1386
|
+
# @param [Array] lines
|
|
1387
|
+
# @param [Object] default
|
|
1388
|
+
# @param [String] indent
|
|
1389
|
+
# @param [String] pname
|
|
1390
|
+
# @param [Object] fallback_type
|
|
1391
|
+
# @return [void]
|
|
1392
|
+
def append_option_lines(lines, default, indent, pname, fallback_type)
|
|
1393
|
+
hash_option_pairs(default).each do |pair|
|
|
1394
|
+
lines << build_option_line(pair, indent, pname, fallback_type)
|
|
1395
|
+
end
|
|
1396
|
+
end
|
|
1397
|
+
|
|
1398
|
+
# Extract hash option pairs from a default value node.
|
|
700
1399
|
#
|
|
701
1400
|
# @note module_function: when included, also defines #hash_option_pairs (instance visibility: private)
|
|
702
|
-
# @param [
|
|
703
|
-
# @return [
|
|
1401
|
+
# @param [Object] node AST node for the default value, expected to be :hash type
|
|
1402
|
+
# @return [Object]
|
|
704
1403
|
def hash_option_pairs(node)
|
|
705
1404
|
return [] unless node&.type == :hash
|
|
706
1405
|
|
|
707
1406
|
node.children.select { |child| child.is_a?(Parser::AST::Node) && child.type == :pair }
|
|
708
1407
|
end
|
|
709
1408
|
|
|
710
|
-
#
|
|
1409
|
+
# Build an @option line from a hash pair node.
|
|
1410
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1411
|
+
# @private
|
|
1412
|
+
# @param [Object] pair AST pair node containing key and value
|
|
1413
|
+
# @param [Object] indent indentation string for the doc line
|
|
1414
|
+
# @param [Object] pname the parent parameter name for @option scope
|
|
1415
|
+
# @param [Object] fallback_type default type string when inference fails
|
|
1416
|
+
# @return [Object]
|
|
1417
|
+
def build_option_line(pair, indent, pname, fallback_type)
|
|
1418
|
+
key_node, value_node = pair.children
|
|
1419
|
+
option_key = option_key_name(key_node)
|
|
1420
|
+
option_type = Infer::Literals.type_from_literal(value_node, fallback_type: fallback_type)
|
|
1421
|
+
option_default = node_default_literal(value_node)
|
|
1422
|
+
|
|
1423
|
+
line = "#{indent}# @option #{pname} [#{option_type}] :#{option_key}"
|
|
1424
|
+
line += " (#{option_default})" if option_default
|
|
1425
|
+
line += ' Description of this option.'
|
|
1426
|
+
line
|
|
1427
|
+
end
|
|
1428
|
+
|
|
1429
|
+
# Extract the option key name from a hash key node.
|
|
711
1430
|
#
|
|
712
1431
|
# @note module_function: when included, also defines #option_key_name (instance visibility: private)
|
|
713
|
-
# @param [
|
|
714
|
-
# @return [
|
|
1432
|
+
# @param [Object] key_node AST node for the hash key (:sym or :str type)
|
|
1433
|
+
# @return [Object]
|
|
715
1434
|
def option_key_name(key_node)
|
|
716
1435
|
case key_node&.type
|
|
717
1436
|
when :sym, :str
|
|
718
1437
|
key_node.children.first.to_s
|
|
719
1438
|
else
|
|
720
|
-
key_node&.loc&.expression
|
|
1439
|
+
expression = key_node&.loc&.expression
|
|
1440
|
+
expression&.source.to_s.sub(/\A:/, '')
|
|
721
1441
|
end
|
|
722
1442
|
end
|
|
723
1443
|
|
|
724
|
-
#
|
|
1444
|
+
# Extract the source text of a default value node.
|
|
725
1445
|
#
|
|
726
1446
|
# @note module_function: when included, also defines #node_default_literal (instance visibility: private)
|
|
727
|
-
# @param [
|
|
728
|
-
# @return [
|
|
1447
|
+
# @param [Object] node AST node whose source text to extract
|
|
1448
|
+
# @return [Object]
|
|
729
1449
|
def node_default_literal(node)
|
|
730
|
-
node&.loc&.expression
|
|
1450
|
+
expression = node&.loc&.expression
|
|
1451
|
+
expression&.source
|
|
1452
|
+
end
|
|
1453
|
+
|
|
1454
|
+
# Look up a parameter type from an override map.
|
|
1455
|
+
#
|
|
1456
|
+
# @note module_function: when included, also defines #override_param_type_for (instance visibility: private)
|
|
1457
|
+
# @param [Object] pname the parameter name to look up
|
|
1458
|
+
# @param [Object] override_map hash map of parameter name to override type
|
|
1459
|
+
# @return [Object]
|
|
1460
|
+
def override_param_type_for(pname, override_map)
|
|
1461
|
+
return nil unless override_map
|
|
1462
|
+
|
|
1463
|
+
key = pname.to_s
|
|
1464
|
+
override_map[key] || override_map[:"#{key}"] || override_map["#{key}:"] || override_map[:"#{key}:"]
|
|
731
1465
|
end
|
|
732
1466
|
|
|
733
1467
|
# Extract the parameter name from a `@param` doc line.
|
|
734
1468
|
#
|
|
735
1469
|
# Handles both `"@param [Type] name"` and `"@param name [Type]"` styles.
|
|
736
1470
|
#
|
|
737
|
-
# @note
|
|
1471
|
+
# @note also defines #extract_param_name_from_param_line (instance: private)
|
|
1472
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
738
1473
|
# @param [String] line a `@param` doc line
|
|
739
1474
|
# @return [String, nil] the parameter name or nil
|
|
740
1475
|
def extract_param_name_from_param_line(line)
|
|
@@ -744,17 +1479,182 @@ module Docscribe
|
|
|
744
1479
|
nil
|
|
745
1480
|
end
|
|
746
1481
|
|
|
747
|
-
#
|
|
1482
|
+
# Extract the type from a `@param` tag line.
|
|
748
1483
|
#
|
|
749
|
-
# @note
|
|
750
|
-
# @
|
|
751
|
-
# @
|
|
1484
|
+
# @note also defines #extract_param_type_from_param_line (instance: private)
|
|
1485
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1486
|
+
# @param [String] line a `@param` tag line
|
|
1487
|
+
# @return [String, nil]
|
|
752
1488
|
def extract_param_type_from_param_line(line)
|
|
753
1489
|
if (m = line.match(/@param\s+\[([^\]]+)\]\s+\S+/) || line.match(/@param\s+\S+\s+\[([^\]]+)\]/))
|
|
754
1490
|
m[1]
|
|
755
1491
|
end
|
|
756
1492
|
end
|
|
757
1493
|
|
|
1494
|
+
# Collect missing raise tags for build_missing_merge_result.
|
|
1495
|
+
#
|
|
1496
|
+
# @note module_function: when included, also defines #collect_missing_raises! (instance visibility: private)
|
|
1497
|
+
# @param [Array<String>] lines
|
|
1498
|
+
# @param [Array<Hash>] reasons
|
|
1499
|
+
# @param [Hash] ctx
|
|
1500
|
+
# @return [void]
|
|
1501
|
+
def collect_missing_raises!(lines, reasons, **ctx)
|
|
1502
|
+
return unless ctx[:config].emit_raise_tags?
|
|
1503
|
+
|
|
1504
|
+
inferred = Docscribe::Infer.infer_raises_from_node(ctx[:node])
|
|
1505
|
+
existing = ctx[:info][:raise_types] || {}
|
|
1506
|
+
missing = inferred.reject { |rt| existing[rt] }
|
|
1507
|
+
|
|
1508
|
+
missing.each do |rt|
|
|
1509
|
+
lines << "#{ctx[:indent]}# @raise [#{rt}]\n"
|
|
1510
|
+
reasons << { type: :missing_raise, message: "missing @raise [#{rt}]", extra: { raise_type: rt } }
|
|
1511
|
+
end
|
|
1512
|
+
end
|
|
1513
|
+
|
|
1514
|
+
# Collect missing/updated return tag for build_missing_merge_result.
|
|
1515
|
+
#
|
|
1516
|
+
# @note module_function: when included, also defines #collect_missing_return! (instance visibility: private)
|
|
1517
|
+
# @param [Array<String>] lines
|
|
1518
|
+
# @param [Array<Hash>] reasons
|
|
1519
|
+
# @param [Hash] ctx
|
|
1520
|
+
# @return [void]
|
|
1521
|
+
def collect_missing_return!(lines, reasons, **ctx)
|
|
1522
|
+
return unless ctx[:config].emit_return_tag?(ctx[:scope], ctx[:visibility])
|
|
1523
|
+
|
|
1524
|
+
if !ctx[:info][:has_return]
|
|
1525
|
+
record_missing_return(lines, reasons, ctx)
|
|
1526
|
+
elsif return_type_changed?(ctx)
|
|
1527
|
+
record_updated_return(lines, reasons, ctx)
|
|
1528
|
+
end
|
|
1529
|
+
end
|
|
1530
|
+
|
|
1531
|
+
# Record a missing @return tag and its reason.
|
|
1532
|
+
#
|
|
1533
|
+
# @note module_function: when included, also defines #record_missing_return (instance visibility: private)
|
|
1534
|
+
# @param [Object] lines array of output doc lines being accumulated
|
|
1535
|
+
# @param [Object] reasons array of reason hashes for --explain output
|
|
1536
|
+
# @param [Object] ctx merged context hash with normal_type and indent
|
|
1537
|
+
# @return [Object]
|
|
1538
|
+
def record_missing_return(lines, reasons, ctx)
|
|
1539
|
+
lines << "#{ctx[:indent]}# @return [#{ctx[:normal_type]}]\n"
|
|
1540
|
+
reasons << { type: :missing_return, message: 'missing @return' }
|
|
1541
|
+
end
|
|
1542
|
+
|
|
1543
|
+
# Record an updated @return tag and its reason.
|
|
1544
|
+
#
|
|
1545
|
+
# @note module_function: when included, also defines #record_updated_return (instance visibility: private)
|
|
1546
|
+
# @param [Object] lines array of output doc lines being accumulated
|
|
1547
|
+
# @param [Object] reasons array of reason hashes for --explain output
|
|
1548
|
+
# @param [Object] ctx merged context hash with normal_type and info
|
|
1549
|
+
# @return [Object]
|
|
1550
|
+
def record_updated_return(lines, reasons, ctx)
|
|
1551
|
+
lines << "#{ctx[:indent]}# @return [#{ctx[:normal_type]}]\n" unless ctx[:strategy] == :safe
|
|
1552
|
+
reasons << { type: :updated_return,
|
|
1553
|
+
message: "updated @return from #{ctx[:info][:return_type]} to #{ctx[:normal_type]}" }
|
|
1554
|
+
end
|
|
1555
|
+
|
|
1556
|
+
# Check if the return type changed between existing doc and inferred/signature type.
|
|
1557
|
+
# Compares existing return type to the resolved normal type.
|
|
1558
|
+
#
|
|
1559
|
+
# @note module_function: when included, also defines #return_type_changed? (instance visibility: private)
|
|
1560
|
+
# @param [Object] ctx merged context hash with external_sig, info, and normal_type
|
|
1561
|
+
# @return [Object]
|
|
1562
|
+
def return_type_changed?(ctx)
|
|
1563
|
+
ctx[:external_sig] && ctx[:info][:return_type] && ctx[:info][:return_type] != ctx[:normal_type]
|
|
1564
|
+
end
|
|
1565
|
+
|
|
1566
|
+
# Collect missing rescue conditional returns for build_missing_merge_result.
|
|
1567
|
+
#
|
|
1568
|
+
# @note also defines #collect_missing_rescue_returns! (instance: private)
|
|
1569
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
1570
|
+
# @param [Array<String>] lines
|
|
1571
|
+
# @param [Array<Hash>] reasons
|
|
1572
|
+
# @param [Hash] ctx
|
|
1573
|
+
# @return [void]
|
|
1574
|
+
def collect_missing_rescue_returns!(lines, reasons, **ctx)
|
|
1575
|
+
return unless ctx[:config].emit_rescue_conditional_returns?
|
|
1576
|
+
return if ctx[:info][:has_return]
|
|
1577
|
+
|
|
1578
|
+
ctx[:rescue_specs].each do |exceptions, rtype|
|
|
1579
|
+
lines << "#{ctx[:indent]}# @return [#{rtype}] if #{exceptions.join(', ')}\n"
|
|
1580
|
+
reasons << {
|
|
1581
|
+
type: :missing_return,
|
|
1582
|
+
message: "missing conditional @return for #{exceptions.join(', ')}"
|
|
1583
|
+
}
|
|
1584
|
+
end
|
|
1585
|
+
end
|
|
1586
|
+
|
|
1587
|
+
# Collect missing plugin tags for build_missing_merge_result.
|
|
1588
|
+
#
|
|
1589
|
+
# @note module_function: when included, also defines #collect_missing_plugin_tags! (instance visibility: private)
|
|
1590
|
+
# @param [Array<String>] lines
|
|
1591
|
+
# @param [Array<Hash>] reasons
|
|
1592
|
+
# @param [Hash] ctx
|
|
1593
|
+
# @return [void]
|
|
1594
|
+
def collect_missing_plugin_tags!(lines, reasons, **ctx)
|
|
1595
|
+
plugin_tags = Docscribe::Plugin.run_tag_plugins(build_plugin_context(ctx[:insertion],
|
|
1596
|
+
normal_type: ctx[:normal_type]))
|
|
1597
|
+
plugin_tags.concat(Array(ctx[:override_tags])) if ctx[:override_tags]
|
|
1598
|
+
|
|
1599
|
+
plugin_tags.each { |tag| record_plugin_tag(tag, lines, reasons, ctx) }
|
|
1600
|
+
end
|
|
1601
|
+
|
|
1602
|
+
# Record a missing plugin tag and its reason.
|
|
1603
|
+
#
|
|
1604
|
+
# @note module_function: when included, also defines #record_plugin_tag (instance visibility: private)
|
|
1605
|
+
# @param [Object] tag plugin tag object to render and record
|
|
1606
|
+
# @param [Object] lines array of output doc lines being accumulated
|
|
1607
|
+
# @param [Object] reasons array of reason hashes for --explain output
|
|
1608
|
+
# @param [Object] ctx merged context hash with info and indent
|
|
1609
|
+
# @return [Object]
|
|
1610
|
+
def record_plugin_tag(tag, lines, reasons, ctx)
|
|
1611
|
+
return if ctx[:info][:plugin_tags]&.[](tag.name)
|
|
1612
|
+
|
|
1613
|
+
rendered = render_plugin_tags([tag], ctx[:indent]).first
|
|
1614
|
+
lines << "#{rendered}\n"
|
|
1615
|
+
reasons << { type: :missing_plugin_tag, message: "missing @#{tag.name}" }
|
|
1616
|
+
end
|
|
1617
|
+
|
|
1618
|
+
# Print a debug warning for a failed doc build phase.
|
|
1619
|
+
#
|
|
1620
|
+
# @note module_function: when included, also defines #debug_warn (instance visibility: private)
|
|
1621
|
+
# @param [StandardError] error the error that occurred
|
|
1622
|
+
# @param [Collector::Insertion] insertion the method insertion being processed
|
|
1623
|
+
# @param [String] name the method name
|
|
1624
|
+
# @param [String] phase the processing phase
|
|
1625
|
+
# @return [void]
|
|
1626
|
+
def debug_warn(error, insertion:, name:, phase:)
|
|
1627
|
+
return unless debug?
|
|
1628
|
+
|
|
1629
|
+
where = build_debug_location(insertion, name)
|
|
1630
|
+
warn "Docscribe DEBUG: #{phase} failed at #{where}: #{error.class}: #{error.message}"
|
|
1631
|
+
end
|
|
1632
|
+
|
|
1633
|
+
# Build a human-readable location string for debug output.
|
|
1634
|
+
# Formats as "file.rb:line Container#method" for error reporting.
|
|
1635
|
+
#
|
|
1636
|
+
# @note module_function: when included, also defines #build_debug_location (instance visibility: private)
|
|
1637
|
+
# @param [Object] insertion the collected method insertion object
|
|
1638
|
+
# @param [Object] name the method name string
|
|
1639
|
+
# @return [Object]
|
|
1640
|
+
def build_debug_location(insertion, name)
|
|
1641
|
+
return name.to_s unless insertion
|
|
1642
|
+
|
|
1643
|
+
expr = insertion.node.loc.expression
|
|
1644
|
+
buf = expr.source_buffer.name
|
|
1645
|
+
sym = insertion.scope == :class ? '.' : '#'
|
|
1646
|
+
ctr = insertion.container || 'Object'
|
|
1647
|
+
+"#{buf}:#{expr.line} #{ctr}#{sym}#{name}"
|
|
1648
|
+
end
|
|
1649
|
+
|
|
1650
|
+
# Check whether debug mode is enabled.
|
|
1651
|
+
#
|
|
1652
|
+
# @note module_function: when included, also defines #debug? (instance visibility: private)
|
|
1653
|
+
# @return [Boolean]
|
|
1654
|
+
def debug?
|
|
1655
|
+
ENV['DOCSCRIBE_DEBUG'] == '1'
|
|
1656
|
+
end
|
|
1657
|
+
|
|
758
1658
|
# Build a Plugin::Context from a collected insertion.
|
|
759
1659
|
#
|
|
760
1660
|
# @note module_function
|
|
@@ -765,12 +1665,19 @@ module Docscribe
|
|
|
765
1665
|
# @return [Docscribe::Plugin::Context]
|
|
766
1666
|
def build_plugin_context(insertion, normal_type:)
|
|
767
1667
|
node = insertion.node
|
|
768
|
-
source =
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
''
|
|
772
|
-
end
|
|
1668
|
+
source = safe_node_source(node)
|
|
1669
|
+
new_plugin_context(insertion, node, source, normal_type)
|
|
1670
|
+
end
|
|
773
1671
|
|
|
1672
|
+
# Build a Plugin::Context from parts.
|
|
1673
|
+
#
|
|
1674
|
+
# @note module_function: when included, also defines #new_plugin_context (instance visibility: private)
|
|
1675
|
+
# @param [Object] insertion
|
|
1676
|
+
# @param [Object] node
|
|
1677
|
+
# @param [String] source
|
|
1678
|
+
# @param [String] normal_type
|
|
1679
|
+
# @return [Docscribe::Plugin::Context]
|
|
1680
|
+
def new_plugin_context(insertion, node, source, normal_type)
|
|
774
1681
|
Docscribe::Plugin::Context.new(
|
|
775
1682
|
node: node,
|
|
776
1683
|
container: insertion.container,
|
|
@@ -783,6 +1690,18 @@ module Docscribe
|
|
|
783
1690
|
)
|
|
784
1691
|
end
|
|
785
1692
|
|
|
1693
|
+
# Safely extract source text from a node.
|
|
1694
|
+
#
|
|
1695
|
+
# @note module_function: when included, also defines #safe_node_source (instance visibility: private)
|
|
1696
|
+
# @param [Parser::AST::Node] node
|
|
1697
|
+
# @raise [StandardError]
|
|
1698
|
+
# @return [String]
|
|
1699
|
+
def safe_node_source(node)
|
|
1700
|
+
node.loc.expression.source
|
|
1701
|
+
rescue StandardError
|
|
1702
|
+
''
|
|
1703
|
+
end
|
|
1704
|
+
|
|
786
1705
|
# Render plugin tags as indented comment lines.
|
|
787
1706
|
#
|
|
788
1707
|
# @note module_function
|
|
@@ -797,39 +1716,6 @@ module Docscribe
|
|
|
797
1716
|
"#{indent}# @#{tag.name}#{type_part}#{text_part}"
|
|
798
1717
|
end
|
|
799
1718
|
end
|
|
800
|
-
|
|
801
|
-
# Print a debug warning for a failed doc build phase.
|
|
802
|
-
#
|
|
803
|
-
# @note module_function: when included, also defines #debug_warn (instance visibility: private)
|
|
804
|
-
# @param [StandardError] e the error that occurred
|
|
805
|
-
# @param [Collector::Insertion] insertion the method insertion being processed
|
|
806
|
-
# @param [String] name the method name
|
|
807
|
-
# @param [String] phase the processing phase
|
|
808
|
-
# @return [void]
|
|
809
|
-
def debug_warn(e, insertion:, name:, phase:)
|
|
810
|
-
return unless debug?
|
|
811
|
-
|
|
812
|
-
node = insertion&.node
|
|
813
|
-
buf_name = node&.loc&.expression&.source_buffer&.name || '(unknown)'
|
|
814
|
-
line = node&.loc&.expression&.line
|
|
815
|
-
scope = insertion&.scope
|
|
816
|
-
method_symbol = scope == :class ? '.' : '#'
|
|
817
|
-
container = insertion&.container || 'Object'
|
|
818
|
-
|
|
819
|
-
where = +buf_name.to_s
|
|
820
|
-
where << ":#{line}" if line
|
|
821
|
-
where << " #{container}#{method_symbol}#{name}"
|
|
822
|
-
|
|
823
|
-
warn "Docscribe DEBUG: #{phase} failed at #{where}: #{e.class}: #{e.message}"
|
|
824
|
-
end
|
|
825
|
-
|
|
826
|
-
# Check whether debug mode is enabled.
|
|
827
|
-
#
|
|
828
|
-
# @note module_function: when included, also defines #debug? (instance visibility: private)
|
|
829
|
-
# @return [Boolean]
|
|
830
|
-
def debug?
|
|
831
|
-
ENV['DOCSCRIBE_DEBUG'] == '1'
|
|
832
|
-
end
|
|
833
1719
|
end
|
|
834
1720
|
end
|
|
835
1721
|
end
|