docscribe 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +588 -104
- data/lib/docscribe/cli/check_for_comments.rb +183 -0
- data/lib/docscribe/cli/config_builder.rb +180 -36
- data/lib/docscribe/cli/formatters/json.rb +294 -0
- data/lib/docscribe/cli/formatters/sarif.rb +235 -0
- data/lib/docscribe/cli/formatters/text.rb +208 -0
- data/lib/docscribe/cli/formatters.rb +26 -0
- data/lib/docscribe/cli/generate.rb +296 -125
- data/lib/docscribe/cli/init.rb +58 -14
- data/lib/docscribe/cli/options.rb +410 -133
- data/lib/docscribe/cli/rbs_gen.rb +529 -0
- data/lib/docscribe/cli/run.rb +503 -189
- data/lib/docscribe/cli/sigs.rb +366 -0
- data/lib/docscribe/cli/update_types.rb +103 -0
- data/lib/docscribe/cli.rb +35 -9
- data/lib/docscribe/config/defaults.rb +16 -12
- data/lib/docscribe/config/emit.rb +18 -0
- data/lib/docscribe/config/filtering.rb +37 -31
- data/lib/docscribe/config/loader.rb +20 -13
- data/lib/docscribe/config/plugin.rb +2 -1
- data/lib/docscribe/config/rbs.rb +68 -27
- data/lib/docscribe/config/sorbet.rb +40 -17
- data/lib/docscribe/config/sorting.rb +2 -1
- data/lib/docscribe/config/template.rb +10 -1
- data/lib/docscribe/config/utils.rb +12 -9
- data/lib/docscribe/config.rb +3 -4
- data/lib/docscribe/infer/ast_walk.rb +1 -1
- data/lib/docscribe/infer/constants.rb +15 -0
- data/lib/docscribe/infer/literals.rb +39 -26
- data/lib/docscribe/infer/names.rb +24 -16
- data/lib/docscribe/infer/params.rb +57 -13
- data/lib/docscribe/infer/raises.rb +23 -15
- data/lib/docscribe/infer/returns.rb +784 -199
- data/lib/docscribe/infer.rb +28 -28
- data/lib/docscribe/inline_rewriter/collector.rb +816 -430
- data/lib/docscribe/inline_rewriter/doc_block.rb +323 -150
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1837 -648
- data/lib/docscribe/inline_rewriter/source_helpers.rb +119 -71
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +165 -107
- data/lib/docscribe/inline_rewriter.rb +1144 -727
- data/lib/docscribe/parsing.rb +29 -10
- data/lib/docscribe/plugin/base/collector_plugin.rb +3 -3
- data/lib/docscribe/plugin/base/tag_plugin.rb +1 -2
- data/lib/docscribe/plugin/context.rb +28 -18
- data/lib/docscribe/plugin/registry.rb +49 -23
- data/lib/docscribe/plugin/tag.rb +9 -14
- data/lib/docscribe/plugin.rb +54 -22
- data/lib/docscribe/types/provider_chain.rb +4 -2
- data/lib/docscribe/types/rbs/collection_loader.rb +2 -3
- data/lib/docscribe/types/rbs/provider.rb +127 -62
- data/lib/docscribe/types/rbs/type_formatter.rb +286 -77
- data/lib/docscribe/types/signature.rb +22 -42
- data/lib/docscribe/types/sorbet/base_provider.rb +51 -27
- data/lib/docscribe/types/sorbet/rbi_provider.rb +3 -3
- data/lib/docscribe/types/sorbet/source_provider.rb +3 -2
- data/lib/docscribe/types/yard/formatter.rb +100 -0
- data/lib/docscribe/types/yard/parser.rb +240 -0
- data/lib/docscribe/types/yard/types.rb +52 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +34 -2
|
@@ -12,38 +12,33 @@ module Docscribe
|
|
|
12
12
|
module DocBlock
|
|
13
13
|
module_function
|
|
14
14
|
|
|
15
|
-
# One parsed entry inside a doc block.
|
|
16
|
-
#
|
|
17
|
-
# `kind` is:
|
|
18
|
-
# - `:tag` for a sortable top-level tag entry
|
|
19
|
-
# - `:other` for prose/separators/non-sortable content
|
|
20
15
|
# @!attribute [rw] kind
|
|
21
|
-
# @return [
|
|
22
|
-
# @param [
|
|
16
|
+
# @return [Symbol]
|
|
17
|
+
# @param [Symbol] value
|
|
23
18
|
#
|
|
24
19
|
# @!attribute [rw] tag
|
|
25
|
-
# @return [
|
|
26
|
-
# @param [
|
|
20
|
+
# @return [String]
|
|
21
|
+
# @param [String] value
|
|
27
22
|
#
|
|
28
23
|
# @!attribute [rw] lines
|
|
29
|
-
# @return [
|
|
30
|
-
# @param [
|
|
24
|
+
# @return [Array<String>]
|
|
25
|
+
# @param [Array<String>] value
|
|
31
26
|
#
|
|
32
27
|
# @!attribute [rw] subject
|
|
33
|
-
# @return [
|
|
34
|
-
# @param [
|
|
28
|
+
# @return [String?]
|
|
29
|
+
# @param [String?] value
|
|
35
30
|
#
|
|
36
31
|
# @!attribute [rw] option_owner
|
|
37
|
-
# @return [
|
|
38
|
-
# @param [
|
|
32
|
+
# @return [String?]
|
|
33
|
+
# @param [String?] value
|
|
39
34
|
#
|
|
40
35
|
# @!attribute [rw] generated
|
|
41
|
-
# @return [
|
|
42
|
-
# @param [
|
|
36
|
+
# @return [Boolean]
|
|
37
|
+
# @param [Boolean] value
|
|
43
38
|
#
|
|
44
39
|
# @!attribute [rw] index
|
|
45
|
-
# @return [
|
|
46
|
-
# @param [
|
|
40
|
+
# @return [Integer]
|
|
41
|
+
# @param [Integer] value
|
|
47
42
|
Entry = Struct.new(
|
|
48
43
|
:kind,
|
|
49
44
|
:tag,
|
|
@@ -60,37 +55,28 @@ module Docscribe
|
|
|
60
55
|
# Existing text is preserved exactly. If sorting is enabled, only sortable tag runs
|
|
61
56
|
# are normalized according to the configured tag order.
|
|
62
57
|
#
|
|
63
|
-
# @note module_function:
|
|
58
|
+
# @note module_function: defines #merge (visibility: private)
|
|
64
59
|
# @param [Array<String>] existing_lines existing doc block lines
|
|
65
60
|
# @param [Array<String>] missing_lines generated tag lines to add
|
|
66
61
|
# @param [Boolean] sort_tags whether sortable tags should be reordered
|
|
67
62
|
# @param [Array<String>] tag_order configured sortable tag order
|
|
68
|
-
# @param [Hash] filter_existing
|
|
63
|
+
# @param [Hash<Symbol, Object>] filter_existing tags to filter from existing block
|
|
69
64
|
# @return [Array<String>]
|
|
70
65
|
def merge(existing_lines, missing_lines:, sort_tags:, tag_order:, filter_existing: {})
|
|
71
66
|
existing_entries = parse(existing_lines, tag_order: tag_order)
|
|
72
67
|
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
|
-
|
|
68
|
+
existing_entries = filter_existing_entries(existing_entries, filter_existing)
|
|
82
69
|
entries = existing_entries + missing_entries
|
|
83
70
|
entries = sort(entries, tag_order: tag_order) if sort_tags
|
|
84
|
-
|
|
85
71
|
render(entries)
|
|
86
72
|
end
|
|
87
73
|
|
|
88
74
|
# Parse generated missing tag lines and mark them as generated entries.
|
|
89
75
|
#
|
|
90
|
-
# @note module_function:
|
|
76
|
+
# @note module_function: defines #parse_generated (visibility: private)
|
|
91
77
|
# @param [Array<String>] lines generated lines
|
|
92
78
|
# @param [Array<String>] tag_order configured sortable tag order
|
|
93
|
-
# @return [Array<Entry>]
|
|
79
|
+
# @return [Array<Docscribe::InlineRewriter::DocBlock::Entry>]
|
|
94
80
|
def parse_generated(lines, tag_order:)
|
|
95
81
|
parse(lines, tag_order: tag_order).map do |entry|
|
|
96
82
|
entry.generated = true if entry.kind == :tag
|
|
@@ -98,75 +84,154 @@ module Docscribe
|
|
|
98
84
|
end
|
|
99
85
|
end
|
|
100
86
|
|
|
87
|
+
# Remove existing entries matching the filter criteria (param names or return tag).
|
|
88
|
+
#
|
|
89
|
+
# @note module_function: defines #filter_existing_entries (visibility: private)
|
|
90
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries parsed existing entries
|
|
91
|
+
# @param [Hash<Symbol, Object>] filter_existing filter specification with :param_names and :return keys
|
|
92
|
+
# @return [Array<Docscribe::InlineRewriter::DocBlock::Entry>] filtered entries
|
|
93
|
+
def filter_existing_entries(entries, filter_existing)
|
|
94
|
+
filter_param_names = filter_existing[:param_names] || []
|
|
95
|
+
filter_return = !!filter_existing[:return]
|
|
96
|
+
entries.reject do |entry|
|
|
97
|
+
filter_param_entry?(entry, filter_param_names) || filter_return_entry?(entry, filter_return)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Check whether an entry is a @param tag whose name is in the filter list.
|
|
102
|
+
#
|
|
103
|
+
# @note module_function: defines #filter_param_entry? (visibility: private)
|
|
104
|
+
# @param [Docscribe::InlineRewriter::DocBlock::Entry] entry the entry to check
|
|
105
|
+
# @param [Array<String>] param_names parameter names to filter
|
|
106
|
+
# @return [Boolean]
|
|
107
|
+
def filter_param_entry?(entry, param_names)
|
|
108
|
+
entry.kind == :tag && entry.tag == 'param' && param_names.include?(entry.subject)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Check whether an entry is a @return tag that should be filtered.
|
|
112
|
+
#
|
|
113
|
+
# @note module_function: defines #filter_return_entry? (visibility: private)
|
|
114
|
+
# @param [Docscribe::InlineRewriter::DocBlock::Entry] entry the entry to check
|
|
115
|
+
# @param [Boolean] filter_return whether return tags should be filtered
|
|
116
|
+
# @return [Boolean]
|
|
117
|
+
def filter_return_entry?(entry, filter_return)
|
|
118
|
+
entry.kind == :tag && entry.tag == 'return' && filter_return
|
|
119
|
+
end
|
|
120
|
+
|
|
101
121
|
# Parse a doc block into structured entries.
|
|
102
122
|
#
|
|
103
123
|
# Only tags listed in `tag_order` are treated as sortable tag entries.
|
|
104
124
|
# Other lines become `:other` entries and act as sort boundaries.
|
|
105
125
|
#
|
|
106
|
-
# @note module_function:
|
|
126
|
+
# @note module_function: defines #parse (visibility: private)
|
|
107
127
|
# @param [Array<String>] lines comment block lines
|
|
108
128
|
# @param [Array<String>] tag_order configured sortable tag order
|
|
109
|
-
# @return [Array<Entry>]
|
|
129
|
+
# @return [Array<Docscribe::InlineRewriter::DocBlock::Entry>]
|
|
110
130
|
def parse(lines, tag_order:)
|
|
111
131
|
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
|
|
132
|
+
parse_lines(lines, sortable_tags, entries: [], index: 0)
|
|
133
|
+
end
|
|
131
134
|
|
|
135
|
+
# Iterate through all lines and parse each one into a structured entry.
|
|
136
|
+
#
|
|
137
|
+
# @note module_function: defines #parse_lines (visibility: private)
|
|
138
|
+
# @param [Array<String>] lines comment block lines
|
|
139
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
140
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries accumulated parsed entries
|
|
141
|
+
# @param [Integer] index stable ordering index for entries
|
|
142
|
+
# @return [Array<Docscribe::InlineRewriter::DocBlock::Entry>]
|
|
143
|
+
def parse_lines(lines, sortable_tags, entries:, index:)
|
|
144
|
+
idx = 0
|
|
145
|
+
while idx < lines.length
|
|
146
|
+
idx = parse_one_line(lines, idx, sortable_tags, entries, index)
|
|
132
147
|
index += 1
|
|
133
148
|
end
|
|
134
|
-
|
|
135
149
|
entries
|
|
136
150
|
end
|
|
137
151
|
|
|
152
|
+
# Parse a single line as a sortable tag entry or non-tag content.
|
|
153
|
+
#
|
|
154
|
+
# @note module_function: defines #parse_one_line (visibility: private)
|
|
155
|
+
# @param [Array<String>] lines comment block lines
|
|
156
|
+
# @param [Integer] idx current line index
|
|
157
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
158
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries accumulated parsed entries
|
|
159
|
+
# @param [Integer] index stable ordering index for entries
|
|
160
|
+
# @return [Integer] next line index after parsing
|
|
161
|
+
def parse_one_line(lines, idx, sortable_tags, entries, index)
|
|
162
|
+
if sortable_top_level_tag_line?(lines[idx], sortable_tags)
|
|
163
|
+
entry, idx = consume_tag_entry(lines, idx, index: index, sortable_tags: sortable_tags)
|
|
164
|
+
entries << entry
|
|
165
|
+
else
|
|
166
|
+
entries << build_other_entry(lines[idx], index)
|
|
167
|
+
idx += 1
|
|
168
|
+
end
|
|
169
|
+
idx
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Create an :other entry for a non-tag line (prose, blank separators, etc.).
|
|
173
|
+
#
|
|
174
|
+
# @note module_function: defines #build_other_entry (visibility: private)
|
|
175
|
+
# @param [String] line the comment line
|
|
176
|
+
# @param [Integer] index stable ordering index
|
|
177
|
+
# @return [Docscribe::InlineRewriter::DocBlock::Entry]
|
|
178
|
+
def build_other_entry(line, index)
|
|
179
|
+
Entry.new(kind: :other, lines: [line], generated: false, index: index)
|
|
180
|
+
end
|
|
181
|
+
|
|
138
182
|
# Sort parsed entries by configured tag order, preserving boundaries between tag runs.
|
|
139
183
|
#
|
|
140
|
-
# @note module_function:
|
|
141
|
-
# @param [Array<Entry>] entries parsed entries
|
|
184
|
+
# @note module_function: defines #sort (visibility: private)
|
|
185
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries parsed entries
|
|
142
186
|
# @param [Array<String>] tag_order configured sortable tag order
|
|
143
|
-
# @return [Array<Entry>]
|
|
187
|
+
# @return [Array<Docscribe::InlineRewriter::DocBlock::Entry>]
|
|
144
188
|
def sort(entries, tag_order:)
|
|
145
|
-
out = []
|
|
189
|
+
out = [] #: Array[untyped]
|
|
146
190
|
priority = build_priority(tag_order)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
191
|
+
sort_loop(entries, out, priority)
|
|
192
|
+
out
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Iterate entries, sorting contiguous tag runs while preserving non-tag boundaries.
|
|
196
|
+
#
|
|
197
|
+
# @note module_function: defines #sort_loop (visibility: private)
|
|
198
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries parsed entries to sort
|
|
199
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] out output array for sorted entries
|
|
200
|
+
# @param [Hash<String, Integer>] priority tag priority map
|
|
201
|
+
# @return [void]
|
|
202
|
+
def sort_loop(entries, out, priority)
|
|
203
|
+
idx = 0
|
|
204
|
+
|
|
205
|
+
while idx < entries.length
|
|
206
|
+
if entries[idx].kind == :tag
|
|
207
|
+
run, idx = consume_tag_run(entries, idx)
|
|
156
208
|
out.concat(sort_run(run, priority: priority))
|
|
157
209
|
else
|
|
158
|
-
out << entries[
|
|
159
|
-
|
|
210
|
+
out << entries[idx]
|
|
211
|
+
idx += 1
|
|
160
212
|
end
|
|
161
213
|
end
|
|
214
|
+
end
|
|
162
215
|
|
|
163
|
-
|
|
216
|
+
# Collect a contiguous run of :tag entries starting at idx.
|
|
217
|
+
#
|
|
218
|
+
# @note module_function: defines #consume_tag_run (visibility: private)
|
|
219
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries parsed entries
|
|
220
|
+
# @param [Integer] idx start index
|
|
221
|
+
# @return [(Array<Docscribe::InlineRewriter::DocBlock::Entry>, Integer)]
|
|
222
|
+
def consume_tag_run(entries, idx)
|
|
223
|
+
run = [] #: Array[untyped]
|
|
224
|
+
while idx < entries.length && entries[idx].kind == :tag
|
|
225
|
+
run << entries[idx]
|
|
226
|
+
idx += 1
|
|
227
|
+
end
|
|
228
|
+
[run, idx]
|
|
164
229
|
end
|
|
165
230
|
|
|
166
231
|
# Render parsed entries back into comment lines.
|
|
167
232
|
#
|
|
168
|
-
# @note module_function:
|
|
169
|
-
# @param [Array<Entry>] entries
|
|
233
|
+
# @note module_function: defines #render (visibility: private)
|
|
234
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries contiguous tag run entries
|
|
170
235
|
# @return [Array<String>]
|
|
171
236
|
def render(entries)
|
|
172
237
|
entries.flat_map(&:lines)
|
|
@@ -174,10 +239,10 @@ module Docscribe
|
|
|
174
239
|
|
|
175
240
|
# Sort one contiguous run of sortable tag entries.
|
|
176
241
|
#
|
|
177
|
-
# @note module_function:
|
|
178
|
-
# @param [Array<Entry>] entries contiguous tag run
|
|
179
|
-
# @param [Hash
|
|
180
|
-
# @return [Array<Entry>]
|
|
242
|
+
# @note module_function: defines #sort_run (visibility: private)
|
|
243
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries contiguous tag run
|
|
244
|
+
# @param [Hash<String, Integer>] priority tag priority map
|
|
245
|
+
# @return [Array<Docscribe::InlineRewriter::DocBlock::Entry>]
|
|
181
246
|
def sort_run(entries, priority:)
|
|
182
247
|
groups = build_groups(entries)
|
|
183
248
|
|
|
@@ -190,43 +255,72 @@ module Docscribe
|
|
|
190
255
|
|
|
191
256
|
# Group entries so related `@option` tags stay attached to their owning `@param`.
|
|
192
257
|
#
|
|
193
|
-
# @note module_function:
|
|
194
|
-
# @param [Array<Entry>] entries
|
|
195
|
-
# @return [Array<Array<Entry>>]
|
|
258
|
+
# @note module_function: defines #build_groups (visibility: private)
|
|
259
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries contiguous tag run entries
|
|
260
|
+
# @return [Array<Array<Docscribe::InlineRewriter::DocBlock::Entry>>]
|
|
196
261
|
def build_groups(entries)
|
|
197
|
-
groups = []
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
entry = entries[i]
|
|
262
|
+
groups = [] #: Array[untyped]
|
|
263
|
+
group_entries_loop(entries, groups)
|
|
264
|
+
groups
|
|
265
|
+
end
|
|
202
266
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
267
|
+
# Iterate entries to build sorted groups, attaching @option entries to their @param.
|
|
268
|
+
#
|
|
269
|
+
# @note module_function: defines #group_entries_loop (visibility: private)
|
|
270
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries contiguous tag run entries
|
|
271
|
+
# @param [Array<Array<Docscribe::InlineRewriter::DocBlock::Entry>>] groups accumulated groups
|
|
272
|
+
# @return [void]
|
|
273
|
+
def group_entries_loop(entries, groups)
|
|
274
|
+
idx = 0
|
|
275
|
+
idx = group_one_entry(entries, idx, groups) while idx < entries.length
|
|
276
|
+
end
|
|
206
277
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
278
|
+
# Group a single entry, creating a param group with @option children if applicable.
|
|
279
|
+
#
|
|
280
|
+
# @note module_function: defines #group_one_entry (visibility: private)
|
|
281
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries contiguous tag run entries
|
|
282
|
+
# @param [Integer] idx current entry index
|
|
283
|
+
# @param [Array<Array<Docscribe::InlineRewriter::DocBlock::Entry>>] groups accumulated groups
|
|
284
|
+
# @return [Integer] next index after processing the group
|
|
285
|
+
def group_one_entry(entries, idx, groups)
|
|
286
|
+
entry = entries[idx]
|
|
287
|
+
if entry.tag == 'param'
|
|
288
|
+
group = build_param_group(entries, idx, entry)
|
|
289
|
+
groups << group
|
|
290
|
+
idx + group.size
|
|
291
|
+
else
|
|
292
|
+
groups << [entry]
|
|
293
|
+
idx + 1
|
|
294
|
+
end
|
|
295
|
+
end
|
|
214
296
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
297
|
+
# Build a group starting with a @param entry and including its following @option entries.
|
|
298
|
+
#
|
|
299
|
+
# @note module_function: defines #build_param_group (visibility: private)
|
|
300
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] entries contiguous tag run entries
|
|
301
|
+
# @param [Integer] idx index of the @param entry
|
|
302
|
+
# @param [Docscribe::InlineRewriter::DocBlock::Entry] entry the @param entry
|
|
303
|
+
# @return [Array<Docscribe::InlineRewriter::DocBlock::Entry>] the param group including @option children
|
|
304
|
+
def build_param_group(entries, idx, entry)
|
|
305
|
+
group = [entry]
|
|
306
|
+
idx += 1
|
|
307
|
+
|
|
308
|
+
while idx < entries.length &&
|
|
309
|
+
entries[idx].tag == 'option' &&
|
|
310
|
+
entries[idx].option_owner &&
|
|
311
|
+
entries[idx].option_owner == entry.subject
|
|
312
|
+
group << entries[idx]
|
|
313
|
+
idx += 1
|
|
220
314
|
end
|
|
221
315
|
|
|
222
|
-
|
|
316
|
+
group
|
|
223
317
|
end
|
|
224
318
|
|
|
225
319
|
# Compute the priority of a grouped sortable unit.
|
|
226
320
|
#
|
|
227
|
-
# @note module_function:
|
|
228
|
-
# @param [Array<Entry>] group
|
|
229
|
-
# @param [Hash
|
|
321
|
+
# @note module_function: defines #group_priority (visibility: private)
|
|
322
|
+
# @param [Array<Docscribe::InlineRewriter::DocBlock::Entry>] group tag group array
|
|
323
|
+
# @param [Hash<String, Integer>] priority tag priority map
|
|
230
324
|
# @return [Integer]
|
|
231
325
|
def group_priority(group, priority)
|
|
232
326
|
priority.fetch(group.first.tag, priority.length)
|
|
@@ -234,17 +328,17 @@ module Docscribe
|
|
|
234
328
|
|
|
235
329
|
# Build a tag priority map from configured order.
|
|
236
330
|
#
|
|
237
|
-
# @note module_function:
|
|
238
|
-
# @param [Array<String>] tag_order
|
|
239
|
-
# @return [Hash
|
|
331
|
+
# @note module_function: defines #build_priority (visibility: private)
|
|
332
|
+
# @param [Array<String>] tag_order configured sortable tag order
|
|
333
|
+
# @return [Hash<String, Integer>]
|
|
240
334
|
def build_priority(tag_order)
|
|
241
335
|
normalized_tag_order(tag_order).each_with_index.to_h
|
|
242
336
|
end
|
|
243
337
|
|
|
244
338
|
# Normalize configured tag names by removing leading `@`.
|
|
245
339
|
#
|
|
246
|
-
# @note module_function:
|
|
247
|
-
# @param [Array<String>] tag_order
|
|
340
|
+
# @note module_function: defines #normalized_tag_order (visibility: private)
|
|
341
|
+
# @param [Array<String>] tag_order configured sortable tag order
|
|
248
342
|
# @return [Array<String>]
|
|
249
343
|
def normalized_tag_order(tag_order)
|
|
250
344
|
Array(tag_order).map { |t| t.to_s.sub(/\A@/, '') }
|
|
@@ -255,30 +349,76 @@ module Docscribe
|
|
|
255
349
|
# Continuation lines are comment lines that belong to the same logical tag entry
|
|
256
350
|
# until a new sortable tag line or a blank comment separator is encountered.
|
|
257
351
|
#
|
|
258
|
-
# @note module_function:
|
|
259
|
-
# @param [Array<String>] lines
|
|
260
|
-
# @param [Integer] start_idx
|
|
352
|
+
# @note module_function: defines #consume_tag_entry (visibility: private)
|
|
353
|
+
# @param [Array<String>] lines comment block lines
|
|
354
|
+
# @param [Integer] start_idx index to start scanning from
|
|
261
355
|
# @param [Integer] index stable original index
|
|
262
|
-
# @param [Array<String>] sortable_tags
|
|
263
|
-
# @return [
|
|
356
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
357
|
+
# @return [(Docscribe::InlineRewriter::DocBlock::Entry, Integer)]
|
|
264
358
|
def consume_tag_entry(lines, start_idx, index:, sortable_tags:)
|
|
265
359
|
first = lines[start_idx]
|
|
266
|
-
tag = extract_tag(first)
|
|
360
|
+
tag = extract_tag(first) || ''
|
|
361
|
+
entry_lines = collect_continuation_lines(lines, start_idx + 1, first, sortable_tags)
|
|
362
|
+
i = entry_lines.length + start_idx
|
|
363
|
+
entry = build_tag_entry(first, tag, entry_lines, index)
|
|
364
|
+
[entry, i]
|
|
365
|
+
end
|
|
267
366
|
|
|
268
|
-
|
|
269
|
-
|
|
367
|
+
# Collect the first tag line and all continuation lines belonging to the same entry.
|
|
368
|
+
#
|
|
369
|
+
# @note module_function: defines #collect_continuation_lines (visibility: private)
|
|
370
|
+
# @param [Array<String>] lines comment block lines
|
|
371
|
+
# @param [Integer] start_idx index after the tag line
|
|
372
|
+
# @param [String] first the tag line itself
|
|
373
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
374
|
+
# @return [Array<String>] all lines belonging to this entry
|
|
375
|
+
def collect_continuation_lines(lines, start_idx, first, sortable_tags)
|
|
376
|
+
result = [first]
|
|
377
|
+
add_continuation_lines(lines, start_idx, result, sortable_tags)
|
|
378
|
+
result
|
|
379
|
+
end
|
|
270
380
|
|
|
381
|
+
# Append continuation lines to the result array until a non-continuation line is found.
|
|
382
|
+
#
|
|
383
|
+
# @note module_function: defines #add_continuation_lines (visibility: private)
|
|
384
|
+
# @param [Array<String>] lines comment block lines
|
|
385
|
+
# @param [Integer] start_idx index to start scanning from
|
|
386
|
+
# @param [Array<String>] result accumulated entry lines
|
|
387
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
388
|
+
# @return [void]
|
|
389
|
+
def add_continuation_lines(lines, start_idx, result, sortable_tags)
|
|
390
|
+
i = start_idx
|
|
271
391
|
while i < lines.length
|
|
272
392
|
line = lines[i]
|
|
273
|
-
break
|
|
274
|
-
break if blank_comment_line?(line)
|
|
275
|
-
break unless continuation_comment_line?(line)
|
|
393
|
+
break unless continuation_candidate?(line, sortable_tags)
|
|
276
394
|
|
|
277
|
-
|
|
395
|
+
result << line
|
|
278
396
|
i += 1
|
|
279
397
|
end
|
|
398
|
+
end
|
|
280
399
|
|
|
281
|
-
|
|
400
|
+
# Check whether a line can serve as a continuation of the current tag entry.
|
|
401
|
+
#
|
|
402
|
+
# @note module_function: defines #continuation_candidate? (visibility: private)
|
|
403
|
+
# @param [String] line the line to check
|
|
404
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
405
|
+
# @return [Boolean]
|
|
406
|
+
def continuation_candidate?(line, sortable_tags)
|
|
407
|
+
!sortable_top_level_tag_line?(line, sortable_tags) &&
|
|
408
|
+
!blank_comment_line?(line) &&
|
|
409
|
+
continuation_comment_line?(line)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Build a tag Entry struct with metadata from the parsed tag line and continuation lines.
|
|
413
|
+
#
|
|
414
|
+
# @note module_function: defines #build_tag_entry (visibility: private)
|
|
415
|
+
# @param [String] first the first (tag) line
|
|
416
|
+
# @param [String] tag the extracted tag name
|
|
417
|
+
# @param [Array<String>] entry_lines all lines belonging to this entry
|
|
418
|
+
# @param [Integer] index stable ordering index
|
|
419
|
+
# @return [Docscribe::InlineRewriter::DocBlock::Entry]
|
|
420
|
+
def build_tag_entry(first, tag, entry_lines, index)
|
|
421
|
+
Entry.new(
|
|
282
422
|
kind: :tag,
|
|
283
423
|
tag: tag,
|
|
284
424
|
lines: entry_lines,
|
|
@@ -287,18 +427,16 @@ module Docscribe
|
|
|
287
427
|
generated: false,
|
|
288
428
|
index: index
|
|
289
429
|
)
|
|
290
|
-
|
|
291
|
-
[entry, i]
|
|
292
430
|
end
|
|
293
431
|
|
|
294
432
|
# Extract the grouping subject for a sortable tag.
|
|
295
433
|
#
|
|
296
434
|
# Currently only `@param` entries carry a subject, used to keep `@option` tags attached.
|
|
297
435
|
#
|
|
298
|
-
# @note module_function:
|
|
299
|
-
# @param [String] line
|
|
300
|
-
# @param [String] tag
|
|
301
|
-
# @return [String
|
|
436
|
+
# @note module_function: defines #extract_subject (visibility: private)
|
|
437
|
+
# @param [String] line the line to check
|
|
438
|
+
# @param [String?] tag the extracted tag name
|
|
439
|
+
# @return [String?]
|
|
302
440
|
def extract_subject(line, tag)
|
|
303
441
|
case tag
|
|
304
442
|
when 'param'
|
|
@@ -312,30 +450,65 @@ module Docscribe
|
|
|
312
450
|
# - `@param [Type] name`
|
|
313
451
|
# - `@param name [Type]`
|
|
314
452
|
#
|
|
315
|
-
# @note module_function:
|
|
316
|
-
# @param [String] line
|
|
317
|
-
# @return [String
|
|
453
|
+
# @note module_function: defines #extract_param_name (visibility: private)
|
|
454
|
+
# @param [String] line the line to check
|
|
455
|
+
# @return [String?]
|
|
318
456
|
def extract_param_name(line)
|
|
319
|
-
|
|
320
|
-
|
|
457
|
+
content = line.sub(/^\s*#\s*/, '')
|
|
458
|
+
if (m = content.match(/@param\s+(\S+)\s+\[/))
|
|
459
|
+
return m[1]
|
|
460
|
+
elsif (m = content.match(/@param\s+\[/))
|
|
461
|
+
end0 = m.end(0) #: Integer
|
|
462
|
+
rest = content[(end0 - 1)..] #: String
|
|
463
|
+
type_end = matching_close_bracket(rest)
|
|
464
|
+
return name_after_bracket(rest, type_end) if type_end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
nil
|
|
468
|
+
end
|
|
321
469
|
|
|
470
|
+
# Extract name after type bracket
|
|
471
|
+
#
|
|
472
|
+
# @note module_function: defines #name_after_bracket (visibility: private)
|
|
473
|
+
# @param [String] rest remaining tag content
|
|
474
|
+
# @param [Integer] type_end closing bracket position
|
|
475
|
+
# @return [String?]
|
|
476
|
+
def name_after_bracket(rest, type_end)
|
|
477
|
+
rest[(type_end + 1)..].to_s.strip.split(/\s+/).first
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# Find the index of the matching close bracket for an outermost `[`.
|
|
481
|
+
#
|
|
482
|
+
# @note module_function: defines #matching_close_bracket (visibility: private)
|
|
483
|
+
# @param [String] str string to scan
|
|
484
|
+
# @return [Integer?]
|
|
485
|
+
def matching_close_bracket(str)
|
|
486
|
+
depth = 0
|
|
487
|
+
str.each_char.with_index do |c, i|
|
|
488
|
+
case c
|
|
489
|
+
when '[' then depth += 1
|
|
490
|
+
when ']'
|
|
491
|
+
depth -= 1
|
|
492
|
+
return i if depth.zero?
|
|
493
|
+
end
|
|
494
|
+
end
|
|
322
495
|
nil
|
|
323
496
|
end
|
|
324
497
|
|
|
325
498
|
# Extract the owning options-hash param name from an `@option` line.
|
|
326
499
|
#
|
|
327
|
-
# @note module_function:
|
|
328
|
-
# @param [String] line
|
|
329
|
-
# @return [String
|
|
500
|
+
# @note module_function: defines #extract_option_owner (visibility: private)
|
|
501
|
+
# @param [String] line the line to check
|
|
502
|
+
# @return [String?]
|
|
330
503
|
def extract_option_owner(line)
|
|
331
504
|
line[/^\s*#\s*@option\b\s+(\S+)/, 1]
|
|
332
505
|
end
|
|
333
506
|
|
|
334
507
|
# Whether a line is a sortable top-level tag line.
|
|
335
508
|
#
|
|
336
|
-
# @note module_function:
|
|
337
|
-
# @param [String] line
|
|
338
|
-
# @param [Array<String>] sortable_tags
|
|
509
|
+
# @note module_function: defines #sortable_top_level_tag_line? (visibility: private)
|
|
510
|
+
# @param [String] line the line to check
|
|
511
|
+
# @param [Array<String>] sortable_tags tag names treated as sortable
|
|
339
512
|
# @return [Boolean]
|
|
340
513
|
def sortable_top_level_tag_line?(line, sortable_tags)
|
|
341
514
|
return false unless top_level_tag_line?(line)
|
|
@@ -345,17 +518,17 @@ module Docscribe
|
|
|
345
518
|
|
|
346
519
|
# Extract a top-level tag name without the leading `@`.
|
|
347
520
|
#
|
|
348
|
-
# @note module_function:
|
|
349
|
-
# @param [String] line
|
|
350
|
-
# @return [String
|
|
521
|
+
# @note module_function: defines #extract_tag (visibility: private)
|
|
522
|
+
# @param [String] line the line to check
|
|
523
|
+
# @return [String?]
|
|
351
524
|
def extract_tag(line)
|
|
352
525
|
line[/^\s*#\s*@(\w+)/, 1]
|
|
353
526
|
end
|
|
354
527
|
|
|
355
528
|
# Whether a line begins a top-level YARD-style tag.
|
|
356
529
|
#
|
|
357
|
-
# @note module_function:
|
|
358
|
-
# @param [String] line
|
|
530
|
+
# @note module_function: defines #top_level_tag_line? (visibility: private)
|
|
531
|
+
# @param [String] line the line to check
|
|
359
532
|
# @return [Boolean]
|
|
360
533
|
def top_level_tag_line?(line)
|
|
361
534
|
!!(line =~ /^\s*#\s*@\w+/)
|
|
@@ -363,8 +536,8 @@ module Docscribe
|
|
|
363
536
|
|
|
364
537
|
# Whether a line is any comment line.
|
|
365
538
|
#
|
|
366
|
-
# @note module_function:
|
|
367
|
-
# @param [String] line
|
|
539
|
+
# @note module_function: defines #comment_line? (visibility: private)
|
|
540
|
+
# @param [String] line the line to check
|
|
368
541
|
# @return [Boolean]
|
|
369
542
|
def comment_line?(line)
|
|
370
543
|
!!(line =~ /^\s*#/)
|
|
@@ -372,8 +545,8 @@ module Docscribe
|
|
|
372
545
|
|
|
373
546
|
# Whether a line is a blank comment separator such as `#`.
|
|
374
547
|
#
|
|
375
|
-
# @note module_function:
|
|
376
|
-
# @param [String] line
|
|
548
|
+
# @note module_function: defines #blank_comment_line? (visibility: private)
|
|
549
|
+
# @param [String] line the line to check
|
|
377
550
|
# @return [Boolean]
|
|
378
551
|
def blank_comment_line?(line)
|
|
379
552
|
!!(line =~ /^\s*#\s*$/)
|
|
@@ -381,8 +554,8 @@ module Docscribe
|
|
|
381
554
|
|
|
382
555
|
# Whether a comment line should be treated as a continuation of the previous tag entry.
|
|
383
556
|
#
|
|
384
|
-
# @note module_function:
|
|
385
|
-
# @param [String] line
|
|
557
|
+
# @note module_function: defines #continuation_comment_line? (visibility: private)
|
|
558
|
+
# @param [String] line the line to check
|
|
386
559
|
# @return [Boolean]
|
|
387
560
|
def continuation_comment_line?(line)
|
|
388
561
|
!!(line =~ /^\s*#[ \t]{2,}\S/)
|