docscribe 1.4.0 → 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 +194 -8
- 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 +303 -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 +3 -2
- 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 +1495 -583
- 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 +1052 -457
- 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,636 +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
|
-
#
|
|
31
|
-
# @
|
|
32
|
-
|
|
96
|
+
# @param [Hash] opts additional keyword options forwarded to doc_setup
|
|
97
|
+
# @raise [StandardError]
|
|
98
|
+
# @return [String, nil]
|
|
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
|
|
33
119
|
# @raise [StandardError]
|
|
34
120
|
# @return [String, nil]
|
|
35
|
-
def
|
|
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)
|
|
36
167
|
node = insertion.node
|
|
37
168
|
name = SourceHelpers.node_name(node)
|
|
38
169
|
return nil unless name
|
|
39
170
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
container = insertion.container
|
|
44
|
-
method_symbol = scope == :instance ? '#' : '.'
|
|
171
|
+
setup = extract_base_setup(insertion, name)
|
|
172
|
+
resolve_doc_setup!(setup, node, name, config, opts)
|
|
173
|
+
end
|
|
45
174
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
51
188
|
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
54
205
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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] || []
|
|
66
223
|
)
|
|
224
|
+
end
|
|
67
225
|
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
70
238
|
|
|
71
|
-
|
|
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
|
|
72
250
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
77
265
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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])
|
|
81
282
|
end
|
|
283
|
+
end
|
|
82
284
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
91
296
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
99
307
|
|
|
100
|
-
|
|
101
|
-
|
|
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?
|
|
324
|
+
|
|
325
|
+
line_ary.map { |l| "#{l}\n" }.join
|
|
326
|
+
end
|
|
327
|
+
|
|
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
|
|
337
|
+
|
|
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
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Core tag line merging.
|
|
352
|
+
#
|
|
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.
|
|
363
|
+
#
|
|
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
|
|
371
|
+
|
|
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
|
|
388
|
+
|
|
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)
|
|
402
|
+
|
|
403
|
+
line_ary << ret_line if emit_ret && ret_line
|
|
404
|
+
end
|
|
405
|
+
|
|
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
|
|
424
|
+
|
|
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
|
|
442
|
+
|
|
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
|
|
102
457
|
end
|
|
103
458
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
lines << "#{indent}# @return [#{normal_type}]" if config.emit_return_tag?(scope, visibility)
|
|
459
|
+
param_types[pname] = type_match[1] || 'untyped'
|
|
460
|
+
end
|
|
107
461
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
+
[]
|
|
112
531
|
end
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
116
|
-
lines.concat(render_plugin_tags(plugin_tags, indent))
|
|
117
|
-
lines.map { |l| "#{l}\n" }.join
|
|
118
|
-
rescue StandardError => e
|
|
119
|
-
debug_warn(e, insertion: insertion, name: name || '(unknown)', phase: 'DocBuilder.build')
|
|
120
|
-
nil
|
|
532
|
+
rescue StandardError
|
|
533
|
+
[]
|
|
121
534
|
end
|
|
122
535
|
|
|
123
|
-
#
|
|
124
|
-
# doc-like block.
|
|
536
|
+
# Parse exception names from a `@raise [ExceptionA, ExceptionB]` line.
|
|
125
537
|
#
|
|
126
|
-
#
|
|
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
|
|
544
|
+
|
|
545
|
+
# Build a param name => type map from a method node.
|
|
127
546
|
#
|
|
128
|
-
# @note module_function: when included, also defines #
|
|
129
|
-
# @
|
|
130
|
-
# @param [
|
|
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
|
|
131
551
|
# @param [Docscribe::Config] config
|
|
132
|
-
# @
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
# @raise [StandardError]
|
|
136
|
-
# @return [String, nil]
|
|
137
|
-
def build_merge_additions(insertion, existing_lines:, config:, signature_provider: nil, core_rbs_provider: nil,
|
|
138
|
-
param_types: nil)
|
|
139
|
-
node = insertion.node
|
|
140
|
-
name = SourceHelpers.node_name(node)
|
|
141
|
-
return '' unless name
|
|
552
|
+
# @return [Hash{String => String}, nil]
|
|
553
|
+
def build_param_types_from_node(node, external_sig:, config:)
|
|
554
|
+
return unless node
|
|
142
555
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
scope = insertion.scope
|
|
146
|
-
visibility = insertion.visibility
|
|
556
|
+
args = extract_args_from_node(node)
|
|
557
|
+
return unless args
|
|
147
558
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
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
|
|
153
563
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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)
|
|
576
|
+
end
|
|
577
|
+
end
|
|
161
578
|
|
|
162
|
-
|
|
163
|
-
|
|
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
|
|
164
597
|
|
|
165
|
-
|
|
166
|
-
|
|
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
|
|
167
618
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
+
[]
|
|
174
636
|
end
|
|
637
|
+
end
|
|
175
638
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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 []
|
|
180
651
|
end
|
|
181
652
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
next if pname.nil? || info[:param_names].include?(pname)
|
|
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
|
|
187
657
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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)
|
|
191
677
|
|
|
192
|
-
|
|
193
|
-
inferred = Docscribe::Infer.infer_raises_from_node(node)
|
|
194
|
-
existing = info[:raise_types] || {}
|
|
195
|
-
missing = inferred.reject { |rt| existing[rt] }
|
|
196
|
-
missing.each { |rt| lines << "#{indent}# @raise [#{rt}]" }
|
|
678
|
+
result << pl
|
|
197
679
|
end
|
|
680
|
+
end
|
|
681
|
+
|
|
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?
|
|
692
|
+
|
|
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}]" }
|
|
697
|
+
end
|
|
198
698
|
|
|
199
|
-
|
|
200
|
-
|
|
699
|
+
# Merge return tag line for safe merge mode.
|
|
700
|
+
#
|
|
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.
|
|
715
|
+
#
|
|
716
|
+
# @note module_function: when included, also defines #merge_rescue_return_lines (instance visibility: private)
|
|
717
|
+
# @param [String] indent
|
|
718
|
+
# @param [Array] rescue_specs
|
|
719
|
+
# @param [Docscribe::Config] config
|
|
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]
|
|
725
|
+
|
|
726
|
+
rescue_specs.map do |exceptions, rtype|
|
|
727
|
+
"#{indent}# @return [#{rtype}] if #{exceptions.join(', ')}"
|
|
201
728
|
end
|
|
729
|
+
end
|
|
202
730
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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?
|
|
740
|
+
|
|
741
|
+
add_missing_private(lines, reasons, ctx)
|
|
742
|
+
add_missing_protected(lines, reasons, ctx)
|
|
743
|
+
end
|
|
744
|
+
|
|
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]
|
|
754
|
+
|
|
755
|
+
lines << "#{ctx[:indent]}# @private\n"
|
|
756
|
+
reasons << { type: :missing_visibility, message: 'missing @private' }
|
|
757
|
+
end
|
|
758
|
+
|
|
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
|
|
207
786
|
end
|
|
208
787
|
|
|
209
|
-
|
|
210
|
-
|
|
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
|
|
211
793
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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) }
|
|
216
810
|
end
|
|
217
811
|
|
|
218
|
-
#
|
|
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)
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
|
|
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.
|
|
219
854
|
#
|
|
220
|
-
#
|
|
221
|
-
# - generated missing lines
|
|
222
|
-
# - structured reasons used by `--explain`
|
|
855
|
+
# External signatures take precedence over inferred parameter types.
|
|
223
856
|
#
|
|
224
|
-
# @note module_function: when included, also defines #
|
|
225
|
-
# @param [
|
|
226
|
-
# @param [
|
|
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
|
|
227
861
|
# @param [Docscribe::Config] config
|
|
228
|
-
# @param [
|
|
229
|
-
# @
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
# @return [Hash]
|
|
234
|
-
def build_missing_merge_result(insertion, existing_lines:, config:, signature_provider: nil,
|
|
235
|
-
core_rbs_provider: nil, param_types: nil, strategy: nil)
|
|
236
|
-
node = insertion.node
|
|
237
|
-
name = SourceHelpers.node_name(node)
|
|
238
|
-
return { lines: [], reasons: [] } unless name
|
|
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
|
|
239
867
|
|
|
240
|
-
indent
|
|
241
|
-
|
|
242
|
-
scope = insertion.scope
|
|
243
|
-
visibility = insertion.visibility
|
|
868
|
+
build_all_param_lines(args, indent, external_sig, param_types_override, config)
|
|
869
|
+
end
|
|
244
870
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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))
|
|
889
|
+
end
|
|
890
|
+
params.empty? ? nil : params
|
|
891
|
+
end
|
|
892
|
+
|
|
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
|
|
250
907
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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]
|
|
257
921
|
)
|
|
258
922
|
|
|
259
|
-
|
|
260
|
-
|
|
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
|
|
261
938
|
|
|
262
|
-
|
|
263
|
-
|
|
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
|
|
264
951
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
|
274
970
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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]
|
|
280
1008
|
end
|
|
1009
|
+
end
|
|
281
1010
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
+
[]
|
|
304
1043
|
end
|
|
1044
|
+
end
|
|
305
1045
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
+
[]
|
|
315
1059
|
end
|
|
1060
|
+
end
|
|
316
1061
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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?
|
|
1071
|
+
|
|
1072
|
+
case visibility
|
|
1073
|
+
when :private then ["#{indent}# @private"]
|
|
1074
|
+
when :protected then ["#{indent}# @protected"]
|
|
1075
|
+
else []
|
|
328
1076
|
end
|
|
1077
|
+
end
|
|
329
1078
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
1079
|
+
# Build module_function note line(s) for a full doc block.
|
|
1080
|
+
#
|
|
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
|
|
337
1095
|
end
|
|
338
|
-
end
|
|
339
|
-
plugin_tags = Docscribe::Plugin.run_tag_plugins(
|
|
340
|
-
build_plugin_context(insertion, normal_type: normal_type)
|
|
341
|
-
)
|
|
342
|
-
plugin_tags.each do |tag|
|
|
343
|
-
next if info[:plugin_tags]&.[](tag.name)
|
|
344
1096
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
reasons << { type: :missing_plugin_tag, message: "missing @#{tag.name}" }
|
|
348
|
-
end
|
|
349
|
-
{ lines: lines, reasons: reasons }
|
|
350
|
-
rescue StandardError => e
|
|
351
|
-
debug_warn(e, insertion: insertion, name: name || '(unknown)', phase: 'DocBuilder.build_missing_merge_result')
|
|
352
|
-
{ lines: [], reasons: [] }
|
|
1097
|
+
["#{indent}# @note module_function: when included, also defines ##{name} " \
|
|
1098
|
+
"(instance visibility: #{included_vis})"]
|
|
353
1099
|
end
|
|
354
1100
|
|
|
355
|
-
#
|
|
356
|
-
#
|
|
357
|
-
# Extracts: `@param` names, `@return`, `@raise`, `@private`, `@protected`,
|
|
358
|
-
# `@module_function` notes, and `@option` lines.
|
|
1101
|
+
# Build raise tag lines for a full doc block.
|
|
359
1102
|
#
|
|
360
|
-
# @note module_function: when included, also defines #
|
|
361
|
-
# @param [
|
|
362
|
-
# @
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return_type = nil
|
|
368
|
-
has_private = false
|
|
369
|
-
has_protected = false
|
|
370
|
-
has_module_function_note = false
|
|
371
|
-
raise_types = {}
|
|
372
|
-
plugin_tags = {}
|
|
373
|
-
|
|
374
|
-
Array(lines).each do |line|
|
|
375
|
-
if (m = line.match(/^\s*#\s*@(\w+)\b/))
|
|
376
|
-
plugin_tags[m[1]] = true
|
|
377
|
-
end
|
|
378
|
-
if (pname = extract_param_name_from_param_line(line))
|
|
379
|
-
param_names[pname] = true
|
|
380
|
-
if (type_match = line.match(/@param\s+\[([^\]]+)\]\s+\S+/) || line.match(/@param\s+\S+\s+\[([^\]]+)\]/))
|
|
381
|
-
param_types[pname] = type_match[1]
|
|
382
|
-
end
|
|
383
|
-
end
|
|
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?
|
|
384
1110
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (m = line.match(/@return\s+\[([^\]]+)\]/))
|
|
388
|
-
return_type = m[1]
|
|
389
|
-
end
|
|
390
|
-
end
|
|
391
|
-
has_private ||= line.match?(/^\s*#\s*@private\b/)
|
|
392
|
-
has_protected ||= line.match?(/^\s*#\s*@protected\b/)
|
|
393
|
-
has_module_function_note ||= line.match?(/^\s*#\s*@note\s+module_function:/)
|
|
1111
|
+
raise_types.map { |rt| "#{indent}# @raise [#{rt}]" }
|
|
1112
|
+
end
|
|
394
1113
|
|
|
395
|
-
|
|
396
|
-
|
|
1114
|
+
# Build return tag line for a full doc block.
|
|
1115
|
+
#
|
|
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)
|
|
397
1125
|
|
|
398
|
-
{
|
|
399
|
-
param_names: param_names,
|
|
400
|
-
param_types: param_types,
|
|
401
|
-
has_return: has_return,
|
|
402
|
-
return_type: return_type,
|
|
403
|
-
raise_types: raise_types,
|
|
404
|
-
has_private: has_private,
|
|
405
|
-
has_protected: has_protected,
|
|
406
|
-
has_module_function_note: has_module_function_note,
|
|
407
|
-
plugin_tags: plugin_tags
|
|
408
|
-
}
|
|
1126
|
+
"#{indent}# @return [#{normal_type}]"
|
|
409
1127
|
end
|
|
410
1128
|
|
|
411
|
-
#
|
|
1129
|
+
# Build rescue conditional return lines for a full doc block.
|
|
412
1130
|
#
|
|
413
|
-
# @note module_function: when included, also defines #
|
|
414
|
-
# @param [String]
|
|
415
|
-
# @
|
|
416
|
-
# @
|
|
417
|
-
# @return [Array]
|
|
418
|
-
def
|
|
419
|
-
return [] unless
|
|
1131
|
+
# @note module_function: when included, also defines #build_rescue_return_lines (instance visibility: private)
|
|
1132
|
+
# @param [String] indent
|
|
1133
|
+
# @param [Array] rescue_specs
|
|
1134
|
+
# @param [Docscribe::Config] config
|
|
1135
|
+
# @return [Array<String>]
|
|
1136
|
+
def build_rescue_return_lines(indent, rescue_specs, config)
|
|
1137
|
+
return [] unless config.emit_rescue_conditional_returns?
|
|
420
1138
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
elsif (m = line.match(/^\s*#\s*@raise\s+([A-Z]\w*(?:::[A-Z]\w*)*)/))
|
|
424
|
-
[m[1]]
|
|
425
|
-
else
|
|
426
|
-
[]
|
|
1139
|
+
rescue_specs.map do |exceptions, rtype|
|
|
1140
|
+
"#{indent}# @return [#{rtype}] if #{exceptions.join(', ')}"
|
|
427
1141
|
end
|
|
428
|
-
rescue StandardError
|
|
429
|
-
[]
|
|
430
1142
|
end
|
|
431
1143
|
|
|
432
|
-
#
|
|
1144
|
+
# Build plugin tag lines for a full doc block.
|
|
433
1145
|
#
|
|
434
|
-
# @note module_function: when included, also defines #
|
|
435
|
-
# @param [
|
|
436
|
-
# @
|
|
437
|
-
|
|
438
|
-
|
|
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)
|
|
439
1156
|
end
|
|
440
1157
|
|
|
441
|
-
# Build a param
|
|
442
|
-
#
|
|
443
|
-
# @note module_function: when included, also defines #build_param_types_from_node (instance visibility: private)
|
|
1158
|
+
# Build a param line for a required argument.
|
|
1159
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
444
1160
|
# @private
|
|
445
|
-
# @param [
|
|
446
|
-
# @param [Object
|
|
447
|
-
# @param [
|
|
448
|
-
# @
|
|
449
|
-
|
|
450
|
-
|
|
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
|
|
451
1175
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
|
457
1194
|
|
|
458
|
-
|
|
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])
|
|
1210
|
+
end
|
|
459
1211
|
|
|
460
|
-
|
|
1212
|
+
# Extract source text from an AST node.
|
|
1213
|
+
#
|
|
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
|
|
461
1221
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
472
|
-
)
|
|
473
|
-
param_types[pname] = ty
|
|
474
|
-
|
|
475
|
-
when :optarg
|
|
476
|
-
pname, default = *a
|
|
477
|
-
pname = pname.to_s
|
|
478
|
-
default_src = default&.loc&.expression&.source
|
|
479
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
480
|
-
Infer.infer_param_type(
|
|
481
|
-
pname,
|
|
482
|
-
default_src,
|
|
483
|
-
fallback_type: config.fallback_type,
|
|
484
|
-
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
485
|
-
)
|
|
486
|
-
param_types[pname] = ty
|
|
487
|
-
|
|
488
|
-
when :kwarg
|
|
489
|
-
pname = a.children.first.to_s
|
|
490
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
491
|
-
Infer.infer_param_type(
|
|
492
|
-
"#{pname}:",
|
|
493
|
-
nil,
|
|
494
|
-
fallback_type: config.fallback_type,
|
|
495
|
-
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
496
|
-
)
|
|
497
|
-
param_types[pname] = ty
|
|
498
|
-
|
|
499
|
-
when :kwoptarg
|
|
500
|
-
pname, default = *a
|
|
501
|
-
pname = pname.to_s
|
|
502
|
-
default_src = default&.loc&.expression&.source
|
|
503
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
504
|
-
Infer.infer_param_type(
|
|
505
|
-
"#{pname}:",
|
|
506
|
-
default_src,
|
|
507
|
-
fallback_type: config.fallback_type,
|
|
508
|
-
treat_options_keyword_as_hash: config.treat_options_keyword_as_hash?
|
|
509
|
-
)
|
|
510
|
-
param_types[pname] = ty
|
|
511
|
-
end
|
|
512
|
-
end
|
|
1222
|
+
# Resolve the infer name string from a param name and infer_name lambda.
|
|
1223
|
+
#
|
|
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
|
|
513
1231
|
|
|
514
|
-
|
|
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])
|
|
515
1248
|
end
|
|
516
1249
|
|
|
517
|
-
# Build
|
|
518
|
-
#
|
|
519
|
-
#
|
|
520
|
-
#
|
|
521
|
-
# @
|
|
522
|
-
# @param [
|
|
523
|
-
# @param [
|
|
524
|
-
# @param [
|
|
525
|
-
# @
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
end
|
|
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
|
|
538
1270
|
|
|
539
|
-
|
|
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
|
|
540
1290
|
|
|
541
|
-
|
|
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])
|
|
1306
|
+
end
|
|
542
1307
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
561
|
-
Infer.infer_param_type(
|
|
562
|
-
pname,
|
|
563
|
-
default_src,
|
|
564
|
-
fallback_type: fallback_type,
|
|
565
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
566
|
-
)
|
|
567
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
568
|
-
|
|
569
|
-
hash_option_pairs(default).each do |pair|
|
|
570
|
-
key_node, value_node = pair.children
|
|
571
|
-
option_key = option_key_name(key_node)
|
|
572
|
-
option_type = Infer::Literals.type_from_literal(value_node, fallback_type: fallback_type)
|
|
573
|
-
option_default = node_default_literal(value_node)
|
|
574
|
-
|
|
575
|
-
line = "#{indent}# @option #{pname} [#{option_type}] :#{option_key}"
|
|
576
|
-
line += " (#{option_default})" if option_default
|
|
577
|
-
line += ' Option documentation.'
|
|
578
|
-
params << line
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
when :kwarg
|
|
582
|
-
pname = a.children.first.to_s
|
|
583
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
584
|
-
Infer.infer_param_type(
|
|
585
|
-
"#{pname}:",
|
|
586
|
-
nil,
|
|
587
|
-
fallback_type: fallback_type,
|
|
588
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
589
|
-
)
|
|
590
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
591
|
-
|
|
592
|
-
when :kwoptarg
|
|
593
|
-
pname, default = *a
|
|
594
|
-
pname = pname.to_s
|
|
595
|
-
default_src = default&.loc&.expression&.source
|
|
596
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
597
|
-
Infer.infer_param_type(
|
|
598
|
-
"#{pname}:",
|
|
599
|
-
default_src,
|
|
600
|
-
fallback_type: fallback_type,
|
|
601
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
602
|
-
)
|
|
603
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
604
|
-
|
|
605
|
-
when :restarg
|
|
606
|
-
pname = (a.children.first || 'args').to_s
|
|
607
|
-
ty =
|
|
608
|
-
if external_sig&.rest_positional&.element_type
|
|
609
|
-
"Array<#{external_sig.rest_positional.element_type}>"
|
|
610
|
-
else
|
|
611
|
-
Infer.infer_param_type(
|
|
612
|
-
"*#{pname}",
|
|
613
|
-
nil,
|
|
614
|
-
fallback_type: fallback_type,
|
|
615
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
616
|
-
)
|
|
617
|
-
end
|
|
618
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
619
|
-
|
|
620
|
-
when :kwrestarg
|
|
621
|
-
pname = (a.children.first || 'kwargs').to_s
|
|
622
|
-
ty = external_sig&.rest_keywords&.type ||
|
|
623
|
-
Infer.infer_param_type(
|
|
624
|
-
"**#{pname}",
|
|
625
|
-
nil,
|
|
626
|
-
fallback_type: fallback_type,
|
|
627
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
628
|
-
)
|
|
629
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
630
|
-
|
|
631
|
-
when :blockarg
|
|
632
|
-
pname = (a.children.first || 'block').to_s
|
|
633
|
-
ty = external_sig&.param_types&.[](pname) ||
|
|
634
|
-
Infer.infer_param_type(
|
|
635
|
-
"&#{pname}",
|
|
636
|
-
nil,
|
|
637
|
-
fallback_type: fallback_type,
|
|
638
|
-
treat_options_keyword_as_hash: treat_options_keyword_as_hash
|
|
639
|
-
)
|
|
640
|
-
params << format_param_tag(indent, pname, ty, param_documentation, style: param_tag_style)
|
|
641
|
-
|
|
642
|
-
when :forward_arg
|
|
643
|
-
# skip
|
|
644
|
-
end
|
|
645
|
-
end
|
|
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
|
|
1316
|
+
# @return [Object]
|
|
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
|
|
646
1325
|
|
|
647
|
-
|
|
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)
|
|
648
1358
|
end
|
|
649
1359
|
|
|
650
|
-
# Format a
|
|
1360
|
+
# Format a YARD @param tag line with optional documentation text.
|
|
651
1361
|
#
|
|
652
1362
|
# @note module_function: when included, also defines #format_param_tag (instance visibility: private)
|
|
653
|
-
# @param [
|
|
654
|
-
# @param [
|
|
655
|
-
# @param [
|
|
656
|
-
# @param [
|
|
657
|
-
# @param [
|
|
658
|
-
# @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]
|
|
659
1369
|
def format_param_tag(indent, name, type, documentation, style:)
|
|
660
1370
|
doc = documentation.to_s.strip
|
|
661
1371
|
type = type.to_s
|
|
@@ -670,45 +1380,96 @@ module Docscribe
|
|
|
670
1380
|
doc.empty? ? line : "#{line} #{doc}"
|
|
671
1381
|
end
|
|
672
1382
|
|
|
673
|
-
#
|
|
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.
|
|
674
1399
|
#
|
|
675
1400
|
# @note module_function: when included, also defines #hash_option_pairs (instance visibility: private)
|
|
676
|
-
# @param [
|
|
677
|
-
# @return [
|
|
1401
|
+
# @param [Object] node AST node for the default value, expected to be :hash type
|
|
1402
|
+
# @return [Object]
|
|
678
1403
|
def hash_option_pairs(node)
|
|
679
1404
|
return [] unless node&.type == :hash
|
|
680
1405
|
|
|
681
1406
|
node.children.select { |child| child.is_a?(Parser::AST::Node) && child.type == :pair }
|
|
682
1407
|
end
|
|
683
1408
|
|
|
684
|
-
#
|
|
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.
|
|
685
1430
|
#
|
|
686
1431
|
# @note module_function: when included, also defines #option_key_name (instance visibility: private)
|
|
687
|
-
# @param [
|
|
688
|
-
# @return [
|
|
1432
|
+
# @param [Object] key_node AST node for the hash key (:sym or :str type)
|
|
1433
|
+
# @return [Object]
|
|
689
1434
|
def option_key_name(key_node)
|
|
690
1435
|
case key_node&.type
|
|
691
1436
|
when :sym, :str
|
|
692
1437
|
key_node.children.first.to_s
|
|
693
1438
|
else
|
|
694
|
-
key_node&.loc&.expression
|
|
1439
|
+
expression = key_node&.loc&.expression
|
|
1440
|
+
expression&.source.to_s.sub(/\A:/, '')
|
|
695
1441
|
end
|
|
696
1442
|
end
|
|
697
1443
|
|
|
698
|
-
#
|
|
1444
|
+
# Extract the source text of a default value node.
|
|
699
1445
|
#
|
|
700
1446
|
# @note module_function: when included, also defines #node_default_literal (instance visibility: private)
|
|
701
|
-
# @param [
|
|
702
|
-
# @return [
|
|
1447
|
+
# @param [Object] node AST node whose source text to extract
|
|
1448
|
+
# @return [Object]
|
|
703
1449
|
def node_default_literal(node)
|
|
704
|
-
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}:"]
|
|
705
1465
|
end
|
|
706
1466
|
|
|
707
1467
|
# Extract the parameter name from a `@param` doc line.
|
|
708
1468
|
#
|
|
709
1469
|
# Handles both `"@param [Type] name"` and `"@param name [Type]"` styles.
|
|
710
1470
|
#
|
|
711
|
-
# @note
|
|
1471
|
+
# @note also defines #extract_param_name_from_param_line (instance: private)
|
|
1472
|
+
# @note module_function: when included, also defines # (instance visibility: private)
|
|
712
1473
|
# @param [String] line a `@param` doc line
|
|
713
1474
|
# @return [String, nil] the parameter name or nil
|
|
714
1475
|
def extract_param_name_from_param_line(line)
|
|
@@ -718,17 +1479,182 @@ module Docscribe
|
|
|
718
1479
|
nil
|
|
719
1480
|
end
|
|
720
1481
|
|
|
721
|
-
#
|
|
1482
|
+
# Extract the type from a `@param` tag line.
|
|
722
1483
|
#
|
|
723
|
-
# @note
|
|
724
|
-
# @
|
|
725
|
-
# @
|
|
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]
|
|
726
1488
|
def extract_param_type_from_param_line(line)
|
|
727
1489
|
if (m = line.match(/@param\s+\[([^\]]+)\]\s+\S+/) || line.match(/@param\s+\S+\s+\[([^\]]+)\]/))
|
|
728
1490
|
m[1]
|
|
729
1491
|
end
|
|
730
1492
|
end
|
|
731
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
|
+
|
|
732
1658
|
# Build a Plugin::Context from a collected insertion.
|
|
733
1659
|
#
|
|
734
1660
|
# @note module_function
|
|
@@ -739,12 +1665,19 @@ module Docscribe
|
|
|
739
1665
|
# @return [Docscribe::Plugin::Context]
|
|
740
1666
|
def build_plugin_context(insertion, normal_type:)
|
|
741
1667
|
node = insertion.node
|
|
742
|
-
source =
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
''
|
|
746
|
-
end
|
|
1668
|
+
source = safe_node_source(node)
|
|
1669
|
+
new_plugin_context(insertion, node, source, normal_type)
|
|
1670
|
+
end
|
|
747
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)
|
|
748
1681
|
Docscribe::Plugin::Context.new(
|
|
749
1682
|
node: node,
|
|
750
1683
|
container: insertion.container,
|
|
@@ -757,6 +1690,18 @@ module Docscribe
|
|
|
757
1690
|
)
|
|
758
1691
|
end
|
|
759
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
|
+
|
|
760
1705
|
# Render plugin tags as indented comment lines.
|
|
761
1706
|
#
|
|
762
1707
|
# @note module_function
|
|
@@ -771,39 +1716,6 @@ module Docscribe
|
|
|
771
1716
|
"#{indent}# @#{tag.name}#{type_part}#{text_part}"
|
|
772
1717
|
end
|
|
773
1718
|
end
|
|
774
|
-
|
|
775
|
-
# Print a debug warning for a failed doc build phase.
|
|
776
|
-
#
|
|
777
|
-
# @note module_function: when included, also defines #debug_warn (instance visibility: private)
|
|
778
|
-
# @param [StandardError] e the error that occurred
|
|
779
|
-
# @param [Collector::Insertion] insertion the method insertion being processed
|
|
780
|
-
# @param [String] name the method name
|
|
781
|
-
# @param [String] phase the processing phase
|
|
782
|
-
# @return [void]
|
|
783
|
-
def debug_warn(e, insertion:, name:, phase:)
|
|
784
|
-
return unless debug?
|
|
785
|
-
|
|
786
|
-
node = insertion&.node
|
|
787
|
-
buf_name = node&.loc&.expression&.source_buffer&.name || '(unknown)'
|
|
788
|
-
line = node&.loc&.expression&.line
|
|
789
|
-
scope = insertion&.scope
|
|
790
|
-
method_symbol = scope == :class ? '.' : '#'
|
|
791
|
-
container = insertion&.container || 'Object'
|
|
792
|
-
|
|
793
|
-
where = +buf_name.to_s
|
|
794
|
-
where << ":#{line}" if line
|
|
795
|
-
where << " #{container}#{method_symbol}#{name}"
|
|
796
|
-
|
|
797
|
-
warn "Docscribe DEBUG: #{phase} failed at #{where}: #{e.class}: #{e.message}"
|
|
798
|
-
end
|
|
799
|
-
|
|
800
|
-
# Check whether debug mode is enabled.
|
|
801
|
-
#
|
|
802
|
-
# @note module_function: when included, also defines #debug? (instance visibility: private)
|
|
803
|
-
# @return [Boolean]
|
|
804
|
-
def debug?
|
|
805
|
-
ENV['DOCSCRIBE_DEBUG'] == '1'
|
|
806
|
-
end
|
|
807
1719
|
end
|
|
808
1720
|
end
|
|
809
1721
|
end
|