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,8 +12,8 @@ module Docscribe
|
|
|
12
12
|
|
|
13
13
|
# Extract the method name from a `:def` or `:defs` node.
|
|
14
14
|
#
|
|
15
|
-
# @note module_function:
|
|
16
|
-
# @param [Parser::AST::Node] node
|
|
15
|
+
# @note module_function: defines #node_name (visibility: private)
|
|
16
|
+
# @param [Parser::AST::Node] node def or defs AST node
|
|
17
17
|
# @return [Symbol, nil]
|
|
18
18
|
def node_name(node)
|
|
19
19
|
case node.type
|
|
@@ -26,9 +26,9 @@ module Docscribe
|
|
|
26
26
|
#
|
|
27
27
|
# Used as the insertion point for generated documentation.
|
|
28
28
|
#
|
|
29
|
-
# @note module_function:
|
|
30
|
-
# @param [Parser::Source::Buffer] buffer
|
|
31
|
-
# @param [Parser::AST::Node] node
|
|
29
|
+
# @note module_function: defines #line_start_range (visibility: private)
|
|
30
|
+
# @param [Parser::Source::Buffer] buffer source buffer for range
|
|
31
|
+
# @param [Parser::AST::Node] node target AST node
|
|
32
32
|
# @return [Parser::Source::Range]
|
|
33
33
|
def line_start_range(buffer, node)
|
|
34
34
|
start_pos = node.loc.expression.begin_pos
|
|
@@ -47,49 +47,22 @@ module Docscribe
|
|
|
47
47
|
#
|
|
48
48
|
# Returns nil if no doc-like block is present.
|
|
49
49
|
#
|
|
50
|
-
# @note module_function:
|
|
51
|
-
# @param [Parser::Source::Buffer] buffer
|
|
50
|
+
# @note module_function: defines #doc_comment_block_info (visibility: private)
|
|
51
|
+
# @param [Parser::Source::Buffer] buffer source buffer to scan
|
|
52
52
|
# @param [Integer] def_bol_pos beginning-of-line position of the target def
|
|
53
|
-
# @return [Hash, nil]
|
|
53
|
+
# @return [Hash<Symbol, Array<String>, Integer, nil>, nil]
|
|
54
54
|
def doc_comment_block_info(buffer, def_bol_pos)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
# Skip blank lines directly above def
|
|
61
|
-
i -= 1 while i >= 0 && lines[i].strip.empty?
|
|
62
|
-
|
|
63
|
-
# Nearest non-blank line must be a comment
|
|
64
|
-
return nil unless i >= 0 && lines[i] =~ /^\s*#/
|
|
65
|
-
|
|
66
|
-
# Walk upward to include the entire contiguous comment block
|
|
67
|
-
end_idx = i
|
|
68
|
-
start_idx = i
|
|
69
|
-
start_idx -= 1 while start_idx >= 0 && lines[start_idx] =~ /^\s*#/
|
|
70
|
-
start_idx += 1
|
|
55
|
+
lines = buffer.source.lines
|
|
56
|
+
def_line_idx = (buffer.source[0...def_bol_pos] || '').count("\n")
|
|
57
|
+
block_range = find_comment_block_range(lines, def_line_idx)
|
|
58
|
+
return nil unless block_range
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
start_idx = block_range[:start_idx]
|
|
61
|
+
end_idx = block_range[:end_idx]
|
|
62
|
+
preserved_start_idx = find_preserved_start_idx(lines, start_idx, end_idx)
|
|
63
|
+
return nil unless doc_marker?(lines, preserved_start_idx..end_idx)
|
|
75
64
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
remaining = lines[removable_start_idx..end_idx]
|
|
79
|
-
return nil unless remaining.any? { |line| doc_marker_line?(line) }
|
|
80
|
-
|
|
81
|
-
start_pos = start_idx.positive? ? lines[0...start_idx].join.length : 0
|
|
82
|
-
doc_start_pos = removable_start_idx.positive? ? lines[0...removable_start_idx].join.length : 0
|
|
83
|
-
end_pos = lines[0..end_idx].join.length
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
lines: lines[start_idx..end_idx],
|
|
87
|
-
preserved_lines: lines[start_idx...removable_start_idx],
|
|
88
|
-
doc_lines: lines[removable_start_idx..end_idx],
|
|
89
|
-
start_pos: start_pos,
|
|
90
|
-
doc_start_pos: doc_start_pos,
|
|
91
|
-
end_pos: end_pos
|
|
92
|
-
}
|
|
65
|
+
build_block_info(lines, start_idx, preserved_start_idx, end_idx)
|
|
93
66
|
end
|
|
94
67
|
|
|
95
68
|
# Compute the removable range for an existing doc-like block above a method.
|
|
@@ -97,42 +70,116 @@ module Docscribe
|
|
|
97
70
|
# Preserved directive lines (such as RuboCop directives or magic comments) are excluded
|
|
98
71
|
# from the returned range.
|
|
99
72
|
#
|
|
100
|
-
# @note module_function:
|
|
101
|
-
# @param [Parser::Source::Buffer] buffer
|
|
73
|
+
# @note module_function: defines #comment_block_removal_range (visibility: private)
|
|
74
|
+
# @param [Parser::Source::Buffer] buffer source buffer to scan
|
|
102
75
|
# @param [Integer] def_bol_pos beginning-of-line position of the target def
|
|
103
76
|
# @return [Parser::Source::Range, nil]
|
|
104
77
|
def comment_block_removal_range(buffer, def_bol_pos)
|
|
105
78
|
src = buffer.source
|
|
106
79
|
lines = src.lines
|
|
107
|
-
def_line_idx = src[0...def_bol_pos].count("\n")
|
|
80
|
+
def_line_idx = (src[0...def_bol_pos] || '').count("\n")
|
|
81
|
+
block_range = find_comment_block_range(lines, def_line_idx)
|
|
82
|
+
return nil unless block_range
|
|
83
|
+
|
|
84
|
+
preserved_start_idx = find_preserved_start_idx(lines, block_range[:start_idx], block_range[:end_idx])
|
|
85
|
+
return nil unless doc_marker?(lines, preserved_start_idx..block_range[:end_idx])
|
|
86
|
+
|
|
87
|
+
compute_removal_range(buffer, lines, preserved_start_idx, def_bol_pos)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Find the range of a contiguous comment block directly above a method definition.
|
|
91
|
+
#
|
|
92
|
+
# Walks upward from def_line_idx, skipping blank lines, then includes all
|
|
93
|
+
# contiguous comment lines.
|
|
94
|
+
#
|
|
95
|
+
# @note module_function: defines #find_comment_block_range (visibility: private)
|
|
96
|
+
# @param [Array<String>] lines source code lines
|
|
97
|
+
# @param [Integer] def_line_idx def line index
|
|
98
|
+
# @return [Hash<Symbol, Integer>, nil]
|
|
99
|
+
def find_comment_block_range(lines, def_line_idx)
|
|
108
100
|
i = def_line_idx - 1
|
|
109
101
|
|
|
110
|
-
# Skip blank lines directly above def
|
|
111
102
|
i -= 1 while i >= 0 && lines[i].strip.empty?
|
|
112
|
-
|
|
113
|
-
# Nearest non-blank line must be a comment to remove anything
|
|
114
103
|
return nil unless i >= 0 && lines[i] =~ /^\s*#/
|
|
115
104
|
|
|
116
|
-
# Walk upward to include the entire contiguous comment block
|
|
117
105
|
start_idx = i
|
|
118
106
|
start_idx -= 1 while start_idx >= 0 && lines[start_idx] =~ /^\s*#/
|
|
119
107
|
start_idx += 1
|
|
120
108
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
removable_start_idx += 1 while removable_start_idx <= i && preserved_comment_line?(lines[removable_start_idx])
|
|
109
|
+
{ start_idx: start_idx, end_idx: i }
|
|
110
|
+
end
|
|
124
111
|
|
|
125
|
-
|
|
126
|
-
|
|
112
|
+
# Find the first index in a comment block after preserved directive-style lines.
|
|
113
|
+
#
|
|
114
|
+
# Preserved lines include RuboCop directives and Ruby magic comments.
|
|
115
|
+
#
|
|
116
|
+
# @note module_function: defines #find_preserved_start_idx (visibility: private)
|
|
117
|
+
# @param [Array<String>] lines source code lines
|
|
118
|
+
# @param [Integer] start_idx block start index
|
|
119
|
+
# @param [Integer] end_idx block end index
|
|
120
|
+
# @return [Integer]
|
|
121
|
+
def find_preserved_start_idx(lines, start_idx, end_idx)
|
|
122
|
+
idx = start_idx
|
|
123
|
+
idx += 1 while idx <= end_idx && preserved_comment_line?(lines[idx])
|
|
124
|
+
idx
|
|
125
|
+
end
|
|
127
126
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
# Whether a comment block range contains documentation markers.
|
|
128
|
+
#
|
|
129
|
+
# @note module_function: defines #doc_marker? (visibility: private)
|
|
130
|
+
# @param [Array<String>] lines source code lines
|
|
131
|
+
# @param [Range<Integer>] range line index range
|
|
132
|
+
# @return [Boolean]
|
|
133
|
+
def doc_marker?(lines, range)
|
|
134
|
+
(lines[range] || []).any? { |line| doc_marker_line?(line) }
|
|
135
|
+
end
|
|
131
136
|
|
|
132
|
-
|
|
137
|
+
# Build block info hash from computed line ranges.
|
|
138
|
+
#
|
|
139
|
+
# @note module_function: defines #build_block_info (visibility: private)
|
|
140
|
+
# @param [Array<String>] lines source code lines
|
|
141
|
+
# @param [Integer] start_idx block start index
|
|
142
|
+
# @param [Integer] preserved_start_idx preserved start index
|
|
143
|
+
# @param [Integer] end_idx block end index
|
|
144
|
+
# @return [Hash<Symbol, Array<String>, Integer, nil>]
|
|
145
|
+
def build_block_info(lines, start_idx, preserved_start_idx, end_idx)
|
|
146
|
+
positions = compute_positions(lines, start_idx, preserved_start_idx, end_idx)
|
|
147
|
+
{
|
|
148
|
+
lines: lines[start_idx..end_idx],
|
|
149
|
+
preserved_lines: lines[start_idx...preserved_start_idx],
|
|
150
|
+
doc_lines: lines[preserved_start_idx..end_idx],
|
|
151
|
+
**positions
|
|
152
|
+
}
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Compute the removal range for preserved start position.
|
|
156
|
+
#
|
|
157
|
+
# @note module_function: defines #compute_removal_range (visibility: private)
|
|
158
|
+
# @param [Parser::Source::Buffer] buffer source buffer for position
|
|
159
|
+
# @param [Array<String>] lines source code lines
|
|
160
|
+
# @param [Integer] preserved_start_idx preserved start index
|
|
161
|
+
# @param [Integer] def_bol_pos beginning-of-line position of the target def
|
|
162
|
+
# @return [Parser::Source::Range]
|
|
163
|
+
def compute_removal_range(buffer, lines, preserved_start_idx, def_bol_pos)
|
|
164
|
+
start_pos = preserved_start_idx.positive? ? (lines[0...preserved_start_idx] || []).join.length : 0
|
|
133
165
|
Parser::Source::Range.new(buffer, start_pos, def_bol_pos)
|
|
134
166
|
end
|
|
135
167
|
|
|
168
|
+
# Compute source positions for a comment block.
|
|
169
|
+
#
|
|
170
|
+
# @note module_function: defines #compute_positions (visibility: private)
|
|
171
|
+
# @param [Array<String>] lines source code lines
|
|
172
|
+
# @param [Integer] start_idx block start index
|
|
173
|
+
# @param [Integer] doc_start_idx doc content start index
|
|
174
|
+
# @param [Integer] end_pos_idx block end position index
|
|
175
|
+
# @return [Hash<Symbol, Integer>]
|
|
176
|
+
def compute_positions(lines, start_idx, doc_start_idx, end_pos_idx)
|
|
177
|
+
start_pos = start_idx.positive? ? (lines[0...start_idx] || []).join.length : 0
|
|
178
|
+
doc_start_pos = doc_start_idx.positive? ? (lines[0...doc_start_idx] || []).join.length : 0
|
|
179
|
+
end_pos = (lines[0..end_pos_idx] || []).join.length
|
|
180
|
+
{ start_pos: start_pos, doc_start_pos: doc_start_pos, end_pos: end_pos }
|
|
181
|
+
end
|
|
182
|
+
|
|
136
183
|
# Whether a comment line should be preserved during aggressive replacement.
|
|
137
184
|
#
|
|
138
185
|
# Preserved lines include:
|
|
@@ -140,8 +187,8 @@ module Docscribe
|
|
|
140
187
|
# - Ruby magic comments
|
|
141
188
|
# - tool directives such as `:nocov:` / `:stopdoc:`
|
|
142
189
|
#
|
|
143
|
-
# @note module_function:
|
|
144
|
-
# @param [String] line
|
|
190
|
+
# @note module_function: defines #preserved_comment_line? (visibility: private)
|
|
191
|
+
# @param [String] line comment line to check
|
|
145
192
|
# @return [Boolean]
|
|
146
193
|
def preserved_comment_line?(line)
|
|
147
194
|
# RuboCop directives
|
|
@@ -166,8 +213,8 @@ module Docscribe
|
|
|
166
213
|
# - Docscribe header lines
|
|
167
214
|
# - YARD tags/directives beginning with `@`
|
|
168
215
|
#
|
|
169
|
-
# @note module_function:
|
|
170
|
-
# @param [String] line
|
|
216
|
+
# @note module_function: defines #doc_marker_line? (visibility: private)
|
|
217
|
+
# @param [String] line comment line to check
|
|
171
218
|
# @return [Boolean]
|
|
172
219
|
def doc_marker_line?(line)
|
|
173
220
|
# Docscribe header line:
|
|
@@ -191,14 +238,14 @@ module Docscribe
|
|
|
191
238
|
#
|
|
192
239
|
# This helper is retained for compatibility/legacy behavior checks.
|
|
193
240
|
#
|
|
194
|
-
# @note module_function:
|
|
195
|
-
# @param [Parser::Source::Buffer] buffer
|
|
196
|
-
# @param [Integer] insert_pos
|
|
241
|
+
# @note module_function: defines #already_has_doc_immediately_above? (visibility: private)
|
|
242
|
+
# @param [Parser::Source::Buffer] buffer source buffer to check
|
|
243
|
+
# @param [Integer] insert_pos insertion position
|
|
197
244
|
# @return [Boolean]
|
|
198
245
|
def already_has_doc_immediately_above?(buffer, insert_pos)
|
|
199
246
|
src = buffer.source
|
|
200
247
|
lines = src.lines
|
|
201
|
-
current_line_index = src[0...insert_pos].count("\n")
|
|
248
|
+
current_line_index = (src[0...insert_pos] || '').count("\n")
|
|
202
249
|
i = current_line_index - 1
|
|
203
250
|
i -= 1 while i >= 0 && lines[i].strip.empty?
|
|
204
251
|
return false if i.negative?
|
|
@@ -210,10 +257,11 @@ module Docscribe
|
|
|
210
257
|
#
|
|
211
258
|
# Tabs and spaces are preserved exactly.
|
|
212
259
|
#
|
|
213
|
-
# @note module_function:
|
|
214
|
-
# @param [Parser::AST::Node] node
|
|
260
|
+
# @note module_function: defines #line_indent (visibility: private)
|
|
261
|
+
# @param [Parser::AST::Node] node target AST node
|
|
215
262
|
# @raise [StandardError]
|
|
216
|
-
# @return [String]
|
|
263
|
+
# @return [String] if StandardError
|
|
264
|
+
# @return [String] if StandardError
|
|
217
265
|
def line_indent(node)
|
|
218
266
|
line = node.loc.expression.source_line
|
|
219
267
|
return '' unless line
|
|
@@ -11,33 +11,30 @@ module Docscribe
|
|
|
11
11
|
module TagSorter
|
|
12
12
|
module_function
|
|
13
13
|
|
|
14
|
-
# One sortable top-level tag entry plus its continuation lines.
|
|
15
14
|
# @!attribute [rw] tag
|
|
16
|
-
# @return [
|
|
17
|
-
# @param [
|
|
15
|
+
# @return [String]
|
|
16
|
+
# @param [String] value
|
|
18
17
|
#
|
|
19
18
|
# @!attribute [rw] lines
|
|
20
|
-
# @return [
|
|
21
|
-
# @param [
|
|
19
|
+
# @return [Array<String>]
|
|
20
|
+
# @param [Array<String>] value
|
|
22
21
|
#
|
|
23
22
|
# @!attribute [rw] param_name
|
|
24
|
-
# @return [
|
|
25
|
-
# @param [
|
|
23
|
+
# @return [String?]
|
|
24
|
+
# @param [String?] value
|
|
26
25
|
#
|
|
27
26
|
# @!attribute [rw] option_owner
|
|
28
|
-
# @return [
|
|
29
|
-
# @param [
|
|
27
|
+
# @return [String?]
|
|
28
|
+
# @param [String?] value
|
|
30
29
|
#
|
|
31
30
|
# @!attribute [rw] index
|
|
32
|
-
# @return [
|
|
33
|
-
# @param [
|
|
31
|
+
# @return [Integer]
|
|
32
|
+
# @param [Integer] value
|
|
34
33
|
Entry = Struct.new(:tag, :lines, :param_name, :option_owner, :index, keyword_init: true)
|
|
35
34
|
|
|
36
|
-
# Sort
|
|
35
|
+
# Sort
|
|
37
36
|
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# @note module_function: when included, also defines #sort (instance visibility: private)
|
|
37
|
+
# @note module_function: defines #sort (visibility: private)
|
|
41
38
|
# @param [Array<String>] lines comment block lines
|
|
42
39
|
# @param [Array<String>] tag_order configured tag order
|
|
43
40
|
# @return [Array<String>]
|
|
@@ -47,50 +44,69 @@ module Docscribe
|
|
|
47
44
|
segments.flat_map { |seg| sort_segment(seg, priority: priority) }
|
|
48
45
|
end
|
|
49
46
|
|
|
50
|
-
# Build
|
|
47
|
+
# Build priority
|
|
51
48
|
#
|
|
52
|
-
# @note module_function:
|
|
53
|
-
# @param [Array<String>] tag_order
|
|
54
|
-
# @return [Hash
|
|
49
|
+
# @note module_function: defines #build_priority (visibility: private)
|
|
50
|
+
# @param [Array<String>] tag_order configured tag order
|
|
51
|
+
# @return [Hash<String, Integer>]
|
|
55
52
|
def build_priority(tag_order)
|
|
56
53
|
Array(tag_order).map { |t| t.to_s.sub(/\A@/, '') }
|
|
57
54
|
.each_with_index
|
|
58
55
|
.to_h
|
|
59
56
|
end
|
|
60
57
|
|
|
61
|
-
# Parse
|
|
58
|
+
# Parse segments
|
|
62
59
|
#
|
|
63
|
-
# @note module_function:
|
|
64
|
-
# @param [Array<String>] lines
|
|
65
|
-
# @return [Array<Hash
|
|
60
|
+
# @note module_function: defines #parse_segments (visibility: private)
|
|
61
|
+
# @param [Array<String>] lines comment block lines
|
|
62
|
+
# @return [Array<Hash<Symbol, Object>>]
|
|
66
63
|
def parse_segments(lines)
|
|
67
|
-
segments = []
|
|
64
|
+
segments = [] #: Array[untyped]
|
|
68
65
|
i = 0
|
|
69
66
|
|
|
70
|
-
while i < lines.length
|
|
71
|
-
|
|
67
|
+
i = advance_parse(lines, i, segments) while i < lines.length
|
|
68
|
+
|
|
69
|
+
segments
|
|
70
|
+
end
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
# Advance parse
|
|
73
|
+
#
|
|
74
|
+
# @note module_function: defines #advance_parse (visibility: private)
|
|
75
|
+
# @param [Array<String>] lines comment block lines
|
|
76
|
+
# @param [Integer] idx current parse index
|
|
77
|
+
# @param [Array<Hash<Symbol, Object>>] segments accumulated parsed segments
|
|
78
|
+
# @return [Integer] new index after processing
|
|
79
|
+
def advance_parse(lines, idx, segments)
|
|
80
|
+
if top_level_tag_line?(lines[idx])
|
|
81
|
+
consume_tag_run(lines, idx, segments)
|
|
82
|
+
else
|
|
83
|
+
segments << { type: :other, lines: [lines[idx]] }
|
|
84
|
+
idx + 1
|
|
84
85
|
end
|
|
86
|
+
end
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
# Consume tag run
|
|
89
|
+
#
|
|
90
|
+
# @note module_function: defines #consume_tag_run (visibility: private)
|
|
91
|
+
# @param [Array<String>] lines comment block lines
|
|
92
|
+
# @param [Integer] idx current index
|
|
93
|
+
# @param [Array<Hash<Symbol, Object>>] segments accumulated segments
|
|
94
|
+
# @return [Integer] new index after consuming the run
|
|
95
|
+
def consume_tag_run(lines, idx, segments)
|
|
96
|
+
entries = [] #: Array[untyped]
|
|
97
|
+
while idx < lines.length && top_level_tag_line?(lines[idx])
|
|
98
|
+
entry, idx = consume_entry(lines, idx)
|
|
99
|
+
entries << entry
|
|
100
|
+
end
|
|
101
|
+
segments << { type: :tag_run, entries: entries }
|
|
102
|
+
idx
|
|
87
103
|
end
|
|
88
104
|
|
|
89
|
-
# Sort
|
|
105
|
+
# Sort segment
|
|
90
106
|
#
|
|
91
|
-
# @note module_function:
|
|
92
|
-
# @param [Hash] segment
|
|
93
|
-
# @param [Hash
|
|
107
|
+
# @note module_function: defines #sort_segment (visibility: private)
|
|
108
|
+
# @param [Hash<Symbol, Object>] segment parsed comment segment
|
|
109
|
+
# @param [Hash<String, Integer>] priority tag priority map
|
|
94
110
|
# @return [Array<String>]
|
|
95
111
|
def sort_segment(segment, priority:)
|
|
96
112
|
return segment[:lines] unless segment[:type] == :tag_run
|
|
@@ -104,125 +120,167 @@ module Docscribe
|
|
|
104
120
|
.flat_map(&:lines)
|
|
105
121
|
end
|
|
106
122
|
|
|
107
|
-
#
|
|
123
|
+
# Group priority
|
|
108
124
|
#
|
|
109
|
-
# @note module_function:
|
|
110
|
-
# @param [Array<Entry>] group
|
|
111
|
-
# @param [Hash
|
|
125
|
+
# @note module_function: defines #group_priority (visibility: private)
|
|
126
|
+
# @param [Array<Docscribe::InlineRewriter::TagSorter::Entry>] group entry group array
|
|
127
|
+
# @param [Hash<String, Integer>] priority tag priority map
|
|
112
128
|
# @return [Integer]
|
|
113
129
|
def group_priority(group, priority)
|
|
114
130
|
first = group.first
|
|
115
131
|
priority.fetch(first.tag, priority.length)
|
|
116
132
|
end
|
|
117
133
|
|
|
118
|
-
# Consume
|
|
134
|
+
# Consume entry
|
|
119
135
|
#
|
|
120
|
-
# @note module_function:
|
|
121
|
-
# @param [Array<String>] lines
|
|
122
|
-
# @param [Integer] start_idx
|
|
123
|
-
# @return [
|
|
136
|
+
# @note module_function: defines #consume_entry (visibility: private)
|
|
137
|
+
# @param [Array<String>] lines comment block lines
|
|
138
|
+
# @param [Integer] start_idx original index of the first line
|
|
139
|
+
# @return [(Docscribe::InlineRewriter::TagSorter::Entry, Integer)]
|
|
124
140
|
def consume_entry(lines, start_idx)
|
|
125
141
|
first = lines[start_idx]
|
|
126
|
-
tag = extract_tag_name(first)
|
|
127
|
-
entry_lines =
|
|
128
|
-
i =
|
|
129
|
-
|
|
130
|
-
while i < lines.length
|
|
131
|
-
line = lines[i]
|
|
142
|
+
tag = extract_tag_name(first) || ''
|
|
143
|
+
entry_lines = collect_continuation_lines(lines, start_idx + 1)
|
|
144
|
+
i = entry_lines.length + start_idx
|
|
132
145
|
|
|
133
|
-
|
|
134
|
-
break if blank_comment_line?(line)
|
|
135
|
-
break unless comment_line?(line)
|
|
146
|
+
entry = build_entry(tag, entry_lines, first, start_idx)
|
|
136
147
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
end
|
|
148
|
+
[entry, i]
|
|
149
|
+
end
|
|
140
150
|
|
|
141
|
-
|
|
151
|
+
# Build entry
|
|
152
|
+
#
|
|
153
|
+
# @note module_function: defines #build_entry (visibility: private)
|
|
154
|
+
# @param [String] tag the extracted tag name
|
|
155
|
+
# @param [Array<String>] entry_lines all lines belonging to this entry
|
|
156
|
+
# @param [String] first the first (tag) line
|
|
157
|
+
# @param [Integer] start_idx original index of the first line
|
|
158
|
+
# @return [Docscribe::InlineRewriter::TagSorter::Entry]
|
|
159
|
+
def build_entry(tag, entry_lines, first, start_idx)
|
|
160
|
+
Entry.new(
|
|
142
161
|
tag: tag,
|
|
143
162
|
lines: entry_lines,
|
|
144
163
|
param_name: extract_param_name(first),
|
|
145
164
|
option_owner: extract_option_owner(first),
|
|
146
165
|
index: start_idx
|
|
147
166
|
)
|
|
167
|
+
end
|
|
148
168
|
|
|
149
|
-
|
|
169
|
+
# Collect continuation lines
|
|
170
|
+
#
|
|
171
|
+
# @note module_function: defines #collect_continuation_lines (visibility: private)
|
|
172
|
+
# @param [Array<String>] lines comment block lines
|
|
173
|
+
# @param [Integer] start_idx original index of the first line
|
|
174
|
+
# @return [Array<String>]
|
|
175
|
+
def collect_continuation_lines(lines, start_idx)
|
|
176
|
+
result = [] #: Array[String]
|
|
177
|
+
i = start_idx
|
|
178
|
+
|
|
179
|
+
while i < lines.length
|
|
180
|
+
line = lines[i]
|
|
181
|
+
break if top_level_tag_line?(line) || blank_comment_line?(line) || !comment_line?(line)
|
|
182
|
+
|
|
183
|
+
result << line
|
|
184
|
+
i += 1
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
result
|
|
150
188
|
end
|
|
151
189
|
|
|
152
|
-
# Group entries
|
|
190
|
+
# Group entries
|
|
153
191
|
#
|
|
154
|
-
# @note module_function:
|
|
155
|
-
# @param [Array<Entry>] entries
|
|
156
|
-
# @return [Array<Array<Entry>>]
|
|
192
|
+
# @note module_function: defines #group_entries (visibility: private)
|
|
193
|
+
# @param [Array<Docscribe::InlineRewriter::TagSorter::Entry>] entries parsed tag entries
|
|
194
|
+
# @return [Array<Array<Docscribe::InlineRewriter::TagSorter::Entry>>]
|
|
157
195
|
def group_entries(entries)
|
|
158
|
-
groups = []
|
|
196
|
+
groups = [] #: Array[untyped]
|
|
159
197
|
i = 0
|
|
160
198
|
|
|
161
199
|
while i < entries.length
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if entry.tag == 'param'
|
|
165
|
-
group = [entry]
|
|
166
|
-
i += 1
|
|
167
|
-
|
|
168
|
-
while i < entries.length &&
|
|
169
|
-
entries[i].tag == 'option' &&
|
|
170
|
-
entries[i].option_owner &&
|
|
171
|
-
entries[i].option_owner == entry.param_name
|
|
172
|
-
group << entries[i]
|
|
173
|
-
i += 1
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
groups << group
|
|
177
|
-
else
|
|
178
|
-
groups << [entry]
|
|
179
|
-
i += 1
|
|
180
|
-
end
|
|
200
|
+
groups << group_entry(entries, i)
|
|
201
|
+
i += 1
|
|
181
202
|
end
|
|
182
203
|
|
|
183
204
|
groups
|
|
184
205
|
end
|
|
185
206
|
|
|
186
|
-
#
|
|
207
|
+
# Group entry
|
|
208
|
+
#
|
|
209
|
+
# @note module_function: defines #group_entry (visibility: private)
|
|
210
|
+
# @param [Array<Docscribe::InlineRewriter::TagSorter::Entry>] entries parsed tag entries
|
|
211
|
+
# @param [Integer] idx index of the entry to group
|
|
212
|
+
# @return [Array<Docscribe::InlineRewriter::TagSorter::Entry>] the entry group
|
|
213
|
+
def group_entry(entries, idx)
|
|
214
|
+
entry = entries[idx]
|
|
215
|
+
if entry.tag == 'param'
|
|
216
|
+
[entry] + collect_option_entries(entries, idx + 1, entry.param_name || '')
|
|
217
|
+
else
|
|
218
|
+
[entry]
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Collect option entries
|
|
223
|
+
#
|
|
224
|
+
# @note module_function: defines #collect_option_entries (visibility: private)
|
|
225
|
+
# @param [Array<Docscribe::InlineRewriter::TagSorter::Entry>] entries parsed tag entries
|
|
226
|
+
# @param [Integer] start_idx original index of the first line
|
|
227
|
+
# @param [String] param_name parent param name
|
|
228
|
+
# @return [Array<Docscribe::InlineRewriter::TagSorter::Entry>]
|
|
229
|
+
def collect_option_entries(entries, start_idx, param_name)
|
|
230
|
+
result = [] #: Array[untyped]
|
|
231
|
+
i = start_idx
|
|
232
|
+
|
|
233
|
+
while i < entries.length &&
|
|
234
|
+
entries[i].tag == 'option' &&
|
|
235
|
+
entries[i].option_owner &&
|
|
236
|
+
entries[i].option_owner == param_name
|
|
237
|
+
result << entries[i]
|
|
238
|
+
i += 1
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
result
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Top level tag line
|
|
187
245
|
#
|
|
188
|
-
# @note module_function:
|
|
189
|
-
# @param [String] line
|
|
246
|
+
# @note module_function: defines #top_level_tag_line? (visibility: private)
|
|
247
|
+
# @param [String] line comment line to check
|
|
190
248
|
# @return [Boolean]
|
|
191
249
|
def top_level_tag_line?(line)
|
|
192
250
|
!!(line =~ /^\s*#\s*@\w+/)
|
|
193
251
|
end
|
|
194
252
|
|
|
195
|
-
#
|
|
253
|
+
# Comment line
|
|
196
254
|
#
|
|
197
|
-
# @note module_function:
|
|
198
|
-
# @param [String] line
|
|
255
|
+
# @note module_function: defines #comment_line? (visibility: private)
|
|
256
|
+
# @param [String] line comment line to check
|
|
199
257
|
# @return [Boolean]
|
|
200
258
|
def comment_line?(line)
|
|
201
259
|
!!(line =~ /^\s*#/)
|
|
202
260
|
end
|
|
203
261
|
|
|
204
|
-
#
|
|
262
|
+
# Blank comment line
|
|
205
263
|
#
|
|
206
|
-
# @note module_function:
|
|
207
|
-
# @param [String] line
|
|
264
|
+
# @note module_function: defines #blank_comment_line? (visibility: private)
|
|
265
|
+
# @param [String] line comment line to check
|
|
208
266
|
# @return [Boolean]
|
|
209
267
|
def blank_comment_line?(line)
|
|
210
268
|
!!(line =~ /^\s*#\s*$/)
|
|
211
269
|
end
|
|
212
270
|
|
|
213
|
-
# Extract tag name
|
|
271
|
+
# Extract tag name
|
|
214
272
|
#
|
|
215
|
-
# @note module_function:
|
|
216
|
-
# @param [String] line
|
|
273
|
+
# @note module_function: defines #extract_tag_name (visibility: private)
|
|
274
|
+
# @param [String] line comment line to parse
|
|
217
275
|
# @return [String, nil]
|
|
218
276
|
def extract_tag_name(line)
|
|
219
277
|
line[/^\s*#\s*@(\w+)/, 1]
|
|
220
278
|
end
|
|
221
279
|
|
|
222
|
-
# Extract
|
|
280
|
+
# Extract param name
|
|
223
281
|
#
|
|
224
|
-
# @note module_function:
|
|
225
|
-
# @param [String] line
|
|
282
|
+
# @note module_function: defines #extract_param_name (visibility: private)
|
|
283
|
+
# @param [String] line param tag line to parse
|
|
226
284
|
# @return [String, nil]
|
|
227
285
|
def extract_param_name(line)
|
|
228
286
|
return Regexp.last_match(1) if line =~ /^\s*#\s*@param\b\s+\[[^\]]+\]\s+(\S+)/
|
|
@@ -231,10 +289,10 @@ module Docscribe
|
|
|
231
289
|
nil
|
|
232
290
|
end
|
|
233
291
|
|
|
234
|
-
# Extract
|
|
292
|
+
# Extract option owner
|
|
235
293
|
#
|
|
236
|
-
# @note module_function:
|
|
237
|
-
# @param [String] line
|
|
294
|
+
# @note module_function: defines #extract_option_owner (visibility: private)
|
|
295
|
+
# @param [String] line option tag line to parse
|
|
238
296
|
# @return [String, nil]
|
|
239
297
|
def extract_option_owner(line)
|
|
240
298
|
line[/^\s*#\s*@option\b\s+(\S+)/, 1]
|