docscribe 1.4.1 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +149 -0
- data/lib/docscribe/cli/config_builder.rb +125 -35
- data/lib/docscribe/cli/generate.rb +288 -117
- data/lib/docscribe/cli/init.rb +49 -13
- data/lib/docscribe/cli/options.rb +302 -127
- data/lib/docscribe/cli/run.rb +391 -135
- data/lib/docscribe/cli.rb +23 -5
- data/lib/docscribe/config/defaults.rb +11 -11
- data/lib/docscribe/config/emit.rb +1 -0
- data/lib/docscribe/config/filtering.rb +24 -11
- data/lib/docscribe/config/loader.rb +7 -4
- data/lib/docscribe/config/plugin.rb +1 -0
- data/lib/docscribe/config/rbs.rb +31 -22
- data/lib/docscribe/config/sorbet.rb +41 -15
- data/lib/docscribe/config/sorting.rb +1 -0
- data/lib/docscribe/config/template.rb +1 -0
- data/lib/docscribe/config/utils.rb +1 -0
- data/lib/docscribe/config.rb +1 -0
- data/lib/docscribe/infer/constants.rb +15 -0
- data/lib/docscribe/infer/literals.rb +43 -25
- data/lib/docscribe/infer/names.rb +24 -15
- data/lib/docscribe/infer/params.rb +52 -6
- data/lib/docscribe/infer/raises.rb +24 -14
- data/lib/docscribe/infer/returns.rb +365 -182
- data/lib/docscribe/infer.rb +10 -9
- data/lib/docscribe/inline_rewriter/collector.rb +766 -375
- data/lib/docscribe/inline_rewriter/doc_block.rb +217 -74
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1488 -602
- data/lib/docscribe/inline_rewriter/source_helpers.rb +100 -52
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +109 -48
- data/lib/docscribe/inline_rewriter.rb +1009 -595
- data/lib/docscribe/plugin/base/collector_plugin.rb +2 -3
- data/lib/docscribe/plugin/base/tag_plugin.rb +1 -1
- data/lib/docscribe/plugin/registry.rb +34 -7
- data/lib/docscribe/plugin.rb +48 -17
- data/lib/docscribe/types/rbs/collection_loader.rb +0 -1
- data/lib/docscribe/types/rbs/provider.rb +75 -26
- data/lib/docscribe/types/rbs/type_formatter.rb +127 -59
- data/lib/docscribe/types/sorbet/base_provider.rb +31 -12
- data/lib/docscribe/version.rb +1 -1
- metadata +2 -2
|
@@ -65,23 +65,14 @@ module Docscribe
|
|
|
65
65
|
# @param [Array<String>] missing_lines generated tag lines to add
|
|
66
66
|
# @param [Boolean] sort_tags whether sortable tags should be reordered
|
|
67
67
|
# @param [Array<String>] tag_order configured sortable tag order
|
|
68
|
-
# @param [Hash] filter_existing
|
|
68
|
+
# @param [Hash] filter_existing tags to filter from existing block
|
|
69
69
|
# @return [Array<String>]
|
|
70
70
|
def merge(existing_lines, missing_lines:, sort_tags:, tag_order:, filter_existing: {})
|
|
71
71
|
existing_entries = parse(existing_lines, tag_order: tag_order)
|
|
72
72
|
missing_entries = parse_generated(missing_lines, tag_order: tag_order)
|
|
73
|
-
|
|
74
|
-
filter_param_names = filter_existing[:param_names] || []
|
|
75
|
-
filter_return = !!filter_existing[:return]
|
|
76
|
-
|
|
77
|
-
existing_entries = existing_entries.reject do |e|
|
|
78
|
-
(e.kind == :tag && e.tag == 'param' && filter_param_names.include?(e.subject)) ||
|
|
79
|
-
(e.kind == :tag && e.tag == 'return' && filter_return)
|
|
80
|
-
end
|
|
81
|
-
|
|
73
|
+
existing_entries = filter_existing_entries(existing_entries, filter_existing)
|
|
82
74
|
entries = existing_entries + missing_entries
|
|
83
75
|
entries = sort(entries, tag_order: tag_order) if sort_tags
|
|
84
|
-
|
|
85
76
|
render(entries)
|
|
86
77
|
end
|
|
87
78
|
|
|
@@ -98,6 +89,40 @@ module Docscribe
|
|
|
98
89
|
end
|
|
99
90
|
end
|
|
100
91
|
|
|
92
|
+
# Remove existing entries matching the filter criteria (param names or return tag).
|
|
93
|
+
#
|
|
94
|
+
# @note module_function: when included, also defines #filter_existing_entries (instance visibility: private)
|
|
95
|
+
# @param [Array<Entry>] entries parsed existing entries
|
|
96
|
+
# @param [Hash] filter_existing filter specification with :param_names and :return keys
|
|
97
|
+
# @return [Array<Entry>] filtered entries
|
|
98
|
+
def filter_existing_entries(entries, filter_existing)
|
|
99
|
+
filter_param_names = filter_existing[:param_names] || []
|
|
100
|
+
filter_return = !!filter_existing[:return]
|
|
101
|
+
entries.reject do |entry|
|
|
102
|
+
filter_param_entry?(entry, filter_param_names) || filter_return_entry?(entry, filter_return)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Check whether an entry is a @param tag whose name is in the filter list.
|
|
107
|
+
#
|
|
108
|
+
# @note module_function: when included, also defines #filter_param_entry? (instance visibility: private)
|
|
109
|
+
# @param [Entry] entry the entry to check
|
|
110
|
+
# @param [Array<String>] param_names parameter names to filter
|
|
111
|
+
# @return [Boolean]
|
|
112
|
+
def filter_param_entry?(entry, param_names)
|
|
113
|
+
entry.kind == :tag && entry.tag == 'param' && param_names.include?(entry.subject)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Check whether an entry is a @return tag that should be filtered.
|
|
117
|
+
#
|
|
118
|
+
# @note module_function: when included, also defines #filter_return_entry? (instance visibility: private)
|
|
119
|
+
# @param [Entry] entry the entry to check
|
|
120
|
+
# @param [Boolean] filter_return whether return tags should be filtered
|
|
121
|
+
# @return [Boolean]
|
|
122
|
+
def filter_return_entry?(entry, filter_return)
|
|
123
|
+
entry.kind == :tag && entry.tag == 'return' && filter_return
|
|
124
|
+
end
|
|
125
|
+
|
|
101
126
|
# Parse a doc block into structured entries.
|
|
102
127
|
#
|
|
103
128
|
# Only tags listed in `tag_order` are treated as sortable tag entries.
|
|
@@ -109,32 +134,56 @@ module Docscribe
|
|
|
109
134
|
# @return [Array<Entry>]
|
|
110
135
|
def parse(lines, tag_order:)
|
|
111
136
|
sortable_tags = normalized_tag_order(tag_order)
|
|
112
|
-
entries
|
|
113
|
-
|
|
114
|
-
index = 0
|
|
115
|
-
|
|
116
|
-
while i < lines.length
|
|
117
|
-
line = lines[i]
|
|
118
|
-
|
|
119
|
-
if sortable_top_level_tag_line?(line, sortable_tags)
|
|
120
|
-
entry, i = consume_tag_entry(lines, i, index: index, sortable_tags: sortable_tags)
|
|
121
|
-
entries << entry
|
|
122
|
-
else
|
|
123
|
-
entries << Entry.new(
|
|
124
|
-
kind: :other,
|
|
125
|
-
lines: [line],
|
|
126
|
-
generated: false,
|
|
127
|
-
index: index
|
|
128
|
-
)
|
|
129
|
-
i += 1
|
|
130
|
-
end
|
|
137
|
+
parse_lines(lines, sortable_tags, entries: [], index: 0)
|
|
138
|
+
end
|
|
131
139
|
|
|
140
|
+
# Iterate through all lines and parse each one into a structured entry.
|
|
141
|
+
#
|
|
142
|
+
# @note module_function: when included, also defines #parse_lines (instance visibility: private)
|
|
143
|
+
# @param [Array<String>] lines comment block lines
|
|
144
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
145
|
+
# @param [Array<Entry>] entries accumulated parsed entries
|
|
146
|
+
# @param [Integer] index stable ordering index for entries
|
|
147
|
+
# @return [Array<Entry>]
|
|
148
|
+
def parse_lines(lines, sortable_tags, entries:, index:)
|
|
149
|
+
idx = 0
|
|
150
|
+
while idx < lines.length
|
|
151
|
+
idx = parse_one_line(lines, idx, sortable_tags, entries, index)
|
|
132
152
|
index += 1
|
|
133
153
|
end
|
|
134
|
-
|
|
135
154
|
entries
|
|
136
155
|
end
|
|
137
156
|
|
|
157
|
+
# Parse a single line as a sortable tag entry or non-tag content.
|
|
158
|
+
#
|
|
159
|
+
# @note module_function: when included, also defines #parse_one_line (instance visibility: private)
|
|
160
|
+
# @param [Array<String>] lines comment block lines
|
|
161
|
+
# @param [Integer] idx current line index
|
|
162
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
163
|
+
# @param [Array<Entry>] entries accumulated parsed entries
|
|
164
|
+
# @param [Integer] index stable ordering index for entries
|
|
165
|
+
# @return [Integer] next line index after parsing
|
|
166
|
+
def parse_one_line(lines, idx, sortable_tags, entries, index)
|
|
167
|
+
if sortable_top_level_tag_line?(lines[idx], sortable_tags)
|
|
168
|
+
entry, idx = consume_tag_entry(lines, idx, index: index, sortable_tags: sortable_tags)
|
|
169
|
+
entries << entry
|
|
170
|
+
else
|
|
171
|
+
entries << build_other_entry(lines[idx], index)
|
|
172
|
+
idx += 1
|
|
173
|
+
end
|
|
174
|
+
idx
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Create an :other entry for a non-tag line (prose, blank separators, etc.).
|
|
178
|
+
#
|
|
179
|
+
# @note module_function: when included, also defines #build_other_entry (instance visibility: private)
|
|
180
|
+
# @param [String] line the comment line
|
|
181
|
+
# @param [Integer] index stable ordering index
|
|
182
|
+
# @return [Entry]
|
|
183
|
+
def build_other_entry(line, index)
|
|
184
|
+
Entry.new(kind: :other, lines: [line], generated: false, index: index)
|
|
185
|
+
end
|
|
186
|
+
|
|
138
187
|
# Sort parsed entries by configured tag order, preserving boundaries between tag runs.
|
|
139
188
|
#
|
|
140
189
|
# @note module_function: when included, also defines #sort (instance visibility: private)
|
|
@@ -142,25 +191,46 @@ module Docscribe
|
|
|
142
191
|
# @param [Array<String>] tag_order configured sortable tag order
|
|
143
192
|
# @return [Array<Entry>]
|
|
144
193
|
def sort(entries, tag_order:)
|
|
145
|
-
out = []
|
|
194
|
+
out = [] #: Array[untyped]
|
|
146
195
|
priority = build_priority(tag_order)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
196
|
+
sort_loop(entries, out, priority)
|
|
197
|
+
out
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Iterate entries, sorting contiguous tag runs while preserving non-tag boundaries.
|
|
201
|
+
#
|
|
202
|
+
# @note module_function: when included, also defines #sort_loop (instance visibility: private)
|
|
203
|
+
# @param [Array<Entry>] entries parsed entries to sort
|
|
204
|
+
# @param [Array<Entry>] out output array for sorted entries
|
|
205
|
+
# @param [Hash{String=>Integer}] priority tag priority map
|
|
206
|
+
# @return [void]
|
|
207
|
+
def sort_loop(entries, out, priority)
|
|
208
|
+
idx = 0
|
|
209
|
+
|
|
210
|
+
while idx < entries.length
|
|
211
|
+
if entries[idx].kind == :tag
|
|
212
|
+
run, idx = consume_tag_run(entries, idx)
|
|
156
213
|
out.concat(sort_run(run, priority: priority))
|
|
157
214
|
else
|
|
158
|
-
out << entries[
|
|
159
|
-
|
|
215
|
+
out << entries[idx]
|
|
216
|
+
idx += 1
|
|
160
217
|
end
|
|
161
218
|
end
|
|
219
|
+
end
|
|
162
220
|
|
|
163
|
-
|
|
221
|
+
# Collect a contiguous run of :tag entries starting at idx.
|
|
222
|
+
#
|
|
223
|
+
# @note module_function: when included, also defines #consume_tag_run (instance visibility: private)
|
|
224
|
+
# @param [Array<Entry>] entries parsed entries
|
|
225
|
+
# @param [Integer] idx start index
|
|
226
|
+
# @return [Array<(Array<Entry>, Integer)>] the tag run and next index
|
|
227
|
+
def consume_tag_run(entries, idx)
|
|
228
|
+
run = [] #: Array[untyped]
|
|
229
|
+
while idx < entries.length && entries[idx].kind == :tag
|
|
230
|
+
run << entries[idx]
|
|
231
|
+
idx += 1
|
|
232
|
+
end
|
|
233
|
+
[run, idx]
|
|
164
234
|
end
|
|
165
235
|
|
|
166
236
|
# Render parsed entries back into comment lines.
|
|
@@ -194,32 +264,61 @@ module Docscribe
|
|
|
194
264
|
# @param [Array<Entry>] entries
|
|
195
265
|
# @return [Array<Array<Entry>>]
|
|
196
266
|
def build_groups(entries)
|
|
197
|
-
groups = []
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
entry = entries[i]
|
|
267
|
+
groups = [] #: Array[untyped]
|
|
268
|
+
group_entries_loop(entries, groups)
|
|
269
|
+
groups
|
|
270
|
+
end
|
|
202
271
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
272
|
+
# Iterate entries to build sorted groups, attaching @option entries to their @param.
|
|
273
|
+
#
|
|
274
|
+
# @note module_function: when included, also defines #group_entries_loop (instance visibility: private)
|
|
275
|
+
# @param [Array<Entry>] entries contiguous tag run entries
|
|
276
|
+
# @param [Array<Array<Entry>>] groups accumulated groups
|
|
277
|
+
# @return [void]
|
|
278
|
+
def group_entries_loop(entries, groups)
|
|
279
|
+
idx = 0
|
|
280
|
+
idx = group_one_entry(entries, idx, groups) while idx < entries.length
|
|
281
|
+
end
|
|
206
282
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
283
|
+
# Group a single entry, creating a param group with @option children if applicable.
|
|
284
|
+
#
|
|
285
|
+
# @note module_function: when included, also defines #group_one_entry (instance visibility: private)
|
|
286
|
+
# @param [Array<Entry>] entries contiguous tag run entries
|
|
287
|
+
# @param [Integer] idx current entry index
|
|
288
|
+
# @param [Array<Array<Entry>>] groups accumulated groups
|
|
289
|
+
# @return [Integer] next index after processing the group
|
|
290
|
+
def group_one_entry(entries, idx, groups)
|
|
291
|
+
entry = entries[idx]
|
|
292
|
+
if entry.tag == 'param'
|
|
293
|
+
group = build_param_group(entries, idx, entry)
|
|
294
|
+
groups << group
|
|
295
|
+
idx + group.size
|
|
296
|
+
else
|
|
297
|
+
groups << [entry]
|
|
298
|
+
idx + 1
|
|
299
|
+
end
|
|
300
|
+
end
|
|
214
301
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
302
|
+
# Build a group starting with a @param entry and including its following @option entries.
|
|
303
|
+
#
|
|
304
|
+
# @note module_function: when included, also defines #build_param_group (instance visibility: private)
|
|
305
|
+
# @param [Array<Entry>] entries contiguous tag run entries
|
|
306
|
+
# @param [Integer] idx index of the @param entry
|
|
307
|
+
# @param [Entry] entry the @param entry
|
|
308
|
+
# @return [Array<Entry>] the param group including @option children
|
|
309
|
+
def build_param_group(entries, idx, entry)
|
|
310
|
+
group = [entry]
|
|
311
|
+
idx += 1
|
|
312
|
+
|
|
313
|
+
while idx < entries.length &&
|
|
314
|
+
entries[idx].tag == 'option' &&
|
|
315
|
+
entries[idx].option_owner &&
|
|
316
|
+
entries[idx].option_owner == entry.subject
|
|
317
|
+
group << entries[idx]
|
|
318
|
+
idx += 1
|
|
220
319
|
end
|
|
221
320
|
|
|
222
|
-
|
|
321
|
+
group
|
|
223
322
|
end
|
|
224
323
|
|
|
225
324
|
# Compute the priority of a grouped sortable unit.
|
|
@@ -264,21 +363,67 @@ module Docscribe
|
|
|
264
363
|
def consume_tag_entry(lines, start_idx, index:, sortable_tags:)
|
|
265
364
|
first = lines[start_idx]
|
|
266
365
|
tag = extract_tag(first)
|
|
366
|
+
entry_lines = collect_continuation_lines(lines, start_idx + 1, first, sortable_tags)
|
|
367
|
+
i = entry_lines.length + start_idx
|
|
368
|
+
entry = build_tag_entry(first, tag, entry_lines, index)
|
|
369
|
+
[entry, i]
|
|
370
|
+
end
|
|
267
371
|
|
|
268
|
-
|
|
269
|
-
|
|
372
|
+
# Collect the first tag line and all continuation lines belonging to the same entry.
|
|
373
|
+
#
|
|
374
|
+
# @note module_function: when included, also defines #collect_continuation_lines (instance visibility: private)
|
|
375
|
+
# @param [Array<String>] lines comment block lines
|
|
376
|
+
# @param [Integer] start_idx index after the tag line
|
|
377
|
+
# @param [String] first the tag line itself
|
|
378
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
379
|
+
# @return [Array<String>] all lines belonging to this entry
|
|
380
|
+
def collect_continuation_lines(lines, start_idx, first, sortable_tags)
|
|
381
|
+
result = [first]
|
|
382
|
+
add_continuation_lines(lines, start_idx, result, sortable_tags)
|
|
383
|
+
result
|
|
384
|
+
end
|
|
270
385
|
|
|
386
|
+
# Append continuation lines to the result array until a non-continuation line is found.
|
|
387
|
+
#
|
|
388
|
+
# @note module_function: when included, also defines #add_continuation_lines (instance visibility: private)
|
|
389
|
+
# @param [Array<String>] lines comment block lines
|
|
390
|
+
# @param [Integer] start_idx index to start scanning from
|
|
391
|
+
# @param [Array<String>] result accumulated entry lines
|
|
392
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
393
|
+
# @return [void]
|
|
394
|
+
def add_continuation_lines(lines, start_idx, result, sortable_tags)
|
|
395
|
+
i = start_idx
|
|
271
396
|
while i < lines.length
|
|
272
397
|
line = lines[i]
|
|
273
|
-
break
|
|
274
|
-
break if blank_comment_line?(line)
|
|
275
|
-
break unless continuation_comment_line?(line)
|
|
398
|
+
break unless continuation_candidate?(line, sortable_tags)
|
|
276
399
|
|
|
277
|
-
|
|
400
|
+
result << line
|
|
278
401
|
i += 1
|
|
279
402
|
end
|
|
403
|
+
end
|
|
280
404
|
|
|
281
|
-
|
|
405
|
+
# Check whether a line can serve as a continuation of the current tag entry.
|
|
406
|
+
#
|
|
407
|
+
# @note module_function: when included, also defines #continuation_candidate? (instance visibility: private)
|
|
408
|
+
# @param [String] line the line to check
|
|
409
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
410
|
+
# @return [Boolean]
|
|
411
|
+
def continuation_candidate?(line, sortable_tags)
|
|
412
|
+
!sortable_top_level_tag_line?(line, sortable_tags) &&
|
|
413
|
+
!blank_comment_line?(line) &&
|
|
414
|
+
continuation_comment_line?(line)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Build a tag Entry struct with metadata from the parsed tag line and continuation lines.
|
|
418
|
+
#
|
|
419
|
+
# @note module_function: when included, also defines #build_tag_entry (instance visibility: private)
|
|
420
|
+
# @param [String] first the first (tag) line
|
|
421
|
+
# @param [String, nil] tag the extracted tag name
|
|
422
|
+
# @param [Array<String>] entry_lines all lines belonging to this entry
|
|
423
|
+
# @param [Integer] index stable ordering index
|
|
424
|
+
# @return [Entry]
|
|
425
|
+
def build_tag_entry(first, tag, entry_lines, index)
|
|
426
|
+
Entry.new(
|
|
282
427
|
kind: :tag,
|
|
283
428
|
tag: tag,
|
|
284
429
|
lines: entry_lines,
|
|
@@ -287,8 +432,6 @@ module Docscribe
|
|
|
287
432
|
generated: false,
|
|
288
433
|
index: index
|
|
289
434
|
)
|
|
290
|
-
|
|
291
|
-
[entry, i]
|
|
292
435
|
end
|
|
293
436
|
|
|
294
437
|
# Extract the grouping subject for a sortable tag.
|