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
|
@@ -34,270 +34,430 @@ module Docscribe
|
|
|
34
34
|
# - `rewrite: true` maps to `strategy: :aggressive`
|
|
35
35
|
module InlineRewriter
|
|
36
36
|
class << self
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# This is the main convenience entry point for library usage.
|
|
37
|
+
# Insert comments
|
|
40
38
|
#
|
|
41
39
|
# @param [String] code Ruby source
|
|
42
|
-
# @param [Symbol
|
|
43
|
-
# @param [Boolean
|
|
44
|
-
# @param [Boolean
|
|
45
|
-
# @param [
|
|
46
|
-
# @param [String] file source name used for parser locations/debugging
|
|
40
|
+
# @param [Symbol?] strategy :safe or :aggressive
|
|
41
|
+
# @param [Boolean?] rewrite compatibility alias for aggressive strategy
|
|
42
|
+
# @param [Boolean?] merge compatibility alias for safe strategy
|
|
43
|
+
# @param [Object] options additional keyword arguments forwarded to rewrite_with_report
|
|
47
44
|
# @return [String]
|
|
48
|
-
def insert_comments(code, strategy: nil, rewrite: nil, merge: nil,
|
|
45
|
+
def insert_comments(code, strategy: nil, rewrite: nil, merge: nil, **options)
|
|
49
46
|
strategy = normalize_strategy(strategy: strategy, rewrite: rewrite, merge: merge)
|
|
50
47
|
|
|
51
|
-
rewrite_with_report(
|
|
52
|
-
code,
|
|
53
|
-
strategy: strategy,
|
|
54
|
-
config: config,
|
|
55
|
-
file: file
|
|
56
|
-
)[:output]
|
|
48
|
+
rewrite_with_report(code, strategy: strategy, **options)[:output]
|
|
57
49
|
end
|
|
58
50
|
|
|
59
|
-
# Rewrite
|
|
60
|
-
#
|
|
61
|
-
# The result hash includes:
|
|
62
|
-
# - `:output` => rewritten source
|
|
63
|
-
# - `:changes` => structured change records used by CLI explanation output
|
|
51
|
+
# Rewrite with report
|
|
64
52
|
#
|
|
65
53
|
# @param [String] code Ruby source
|
|
66
|
-
# @param [Symbol
|
|
67
|
-
# @param [Boolean
|
|
68
|
-
# @param [Boolean
|
|
69
|
-
# @param [
|
|
70
|
-
# @
|
|
71
|
-
|
|
72
|
-
# @raise [Docscribe::ParseError]
|
|
73
|
-
# @raise [StandardError]
|
|
74
|
-
# @return [Hash]
|
|
75
|
-
def rewrite_with_report(code, strategy: nil, rewrite: nil, merge: nil, config: nil,
|
|
76
|
-
core_rbs_provider: nil, file: '(inline)')
|
|
54
|
+
# @param [Symbol?] strategy :safe or :aggressive
|
|
55
|
+
# @param [Boolean?] rewrite compatibility alias for aggressive strategy
|
|
56
|
+
# @param [Boolean?] merge compatibility alias for safe strategy
|
|
57
|
+
# @param [Object] options additional keyword arguments forwarded to downstream helpers
|
|
58
|
+
# @return [Hash<Symbol, String, Array<Hash<Symbol, Object>>>]
|
|
59
|
+
def rewrite_with_report(code, strategy: nil, rewrite: nil, merge: nil, **options)
|
|
77
60
|
strategy = normalize_strategy(strategy: strategy, rewrite: rewrite, merge: merge)
|
|
78
61
|
validate_strategy!(strategy)
|
|
62
|
+
parsed = setup_rewrite_env(code, options)
|
|
63
|
+
pipeline = build_rewrite_pipeline(parsed[:buffer], parsed[:ast])
|
|
64
|
+
dispatch_rewrite_insertions(pipeline, parsed[:buffer],
|
|
65
|
+
config: parsed[:config], signature_provider: parsed[:signature_provider],
|
|
66
|
+
core_rbs_provider: parsed[:core_rbs_provider], strategy: strategy,
|
|
67
|
+
file: parsed[:file])
|
|
68
|
+
{ output: pipeline[:rewriter].process, changes: pipeline[:changes] }
|
|
69
|
+
end
|
|
79
70
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
71
|
+
# Build rewrite pipeline
|
|
72
|
+
#
|
|
73
|
+
# @param [Parser::Source::Buffer] buffer the source buffer being rewritten
|
|
74
|
+
# @param [Parser::AST::Node] ast the parsed AST of the source code
|
|
75
|
+
# @return [Hash<Symbol, Object>]
|
|
76
|
+
def build_rewrite_pipeline(buffer, ast)
|
|
77
|
+
all = collect_insertions(buffer, ast)
|
|
78
|
+
method_overrides_by_pos = {} #: Hash[Integer, untyped]
|
|
79
|
+
all = deduplicate_insertions(all, method_overrides_by_pos: method_overrides_by_pos)
|
|
80
|
+
rewriter = Parser::Source::TreeRewriter.new(buffer) # steep:ignore
|
|
81
|
+
merge_inserts = Hash.new { |h, k| h[k] = [] } #: Hash[Integer, untyped]
|
|
82
|
+
changes = [] #: Array[untyped]
|
|
83
|
+
|
|
84
|
+
{ all: all, method_overrides_by_pos: method_overrides_by_pos, rewriter: rewriter,
|
|
85
|
+
merge_inserts: merge_inserts, changes: changes }
|
|
86
|
+
end
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
# Dispatch rewrite insertions
|
|
89
|
+
#
|
|
90
|
+
# @param [Hash<Symbol, Object>] pipeline the pipeline hash with rewriter, insertions, and tracking state
|
|
91
|
+
# @param [Parser::Source::Buffer] buffer the source buffer being rewritten
|
|
92
|
+
# @param [Object] options additional kwargs (config, signature_provider, core_rbs_provider, strategy, file)
|
|
93
|
+
# @return [void]
|
|
94
|
+
def dispatch_rewrite_insertions(pipeline, buffer, **options)
|
|
95
|
+
pipeline[:all].sort_by { |(kind, ins)| plugin_insertion_pos(kind, ins) }
|
|
96
|
+
.reverse_each do |kind, ins|
|
|
97
|
+
method_name = :"dispatch_#{kind}_insertion"
|
|
98
|
+
send(method_name, ins, pipeline, buffer, **options) if respond_to?(method_name, true)
|
|
91
99
|
end
|
|
92
100
|
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
apply_merge_inserts!(rewriter: pipeline[:rewriter], buffer: buffer, merge_inserts: pipeline[:merge_inserts])
|
|
102
|
+
end
|
|
95
103
|
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
# Dispatch method insertion
|
|
105
|
+
#
|
|
106
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] ins the attribute insertion object
|
|
107
|
+
# @param [Hash<Symbol, Object>] pipeline the pipeline hash with rewriter, insertions, and tracking state
|
|
108
|
+
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
109
|
+
# @param [Object] options the full keyword options hash
|
|
110
|
+
# @return [void]
|
|
111
|
+
def dispatch_method_insertion(ins, pipeline, buffer, **options)
|
|
112
|
+
pos = plugin_insertion_pos(:method, ins)
|
|
113
|
+
method_override = pipeline[:method_overrides_by_pos][pos]
|
|
114
|
+
|
|
115
|
+
apply_method_insertion!(
|
|
116
|
+
rewriter: pipeline[:rewriter], buffer: buffer, insertion: ins,
|
|
117
|
+
config: options[:config], signature_provider: options[:signature_provider],
|
|
118
|
+
core_rbs_provider: options[:core_rbs_provider], strategy: options[:strategy],
|
|
119
|
+
changes: pipeline[:changes], file: options[:file],
|
|
120
|
+
method_override: method_override
|
|
121
|
+
)
|
|
122
|
+
end
|
|
98
123
|
|
|
99
|
-
|
|
100
|
-
|
|
124
|
+
# Dispatch attr insertion
|
|
125
|
+
#
|
|
126
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
127
|
+
# @param [Hash<Symbol, Object>] pipeline the pipeline hash with rewriter, insertions, and tracking state
|
|
128
|
+
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
129
|
+
# @param [Object] options the full keyword options hash
|
|
130
|
+
# @return [void]
|
|
131
|
+
def dispatch_attr_insertion(ins, pipeline, buffer, **options)
|
|
132
|
+
apply_attr_insertion!(
|
|
133
|
+
rewriter: pipeline[:rewriter], buffer: buffer, insertion: ins,
|
|
134
|
+
config: options[:config], signature_provider: options[:signature_provider],
|
|
135
|
+
strategy: options[:strategy], merge_inserts: pipeline[:merge_inserts]
|
|
136
|
+
)
|
|
137
|
+
end
|
|
101
138
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
139
|
+
# Dispatch plugin insertion
|
|
140
|
+
#
|
|
141
|
+
# @param [Hash<Symbol, Object>] ins the attribute insertion object
|
|
142
|
+
# @param [Hash<Symbol, Object>] pipeline the pipeline hash with rewriter, insertions, and tracking state
|
|
143
|
+
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
144
|
+
# @param [Object] options the full keyword options hash
|
|
145
|
+
# @return [void]
|
|
146
|
+
def dispatch_plugin_insertion(ins, pipeline, buffer, **options)
|
|
147
|
+
apply_plugin_insertion!(
|
|
148
|
+
rewriter: pipeline[:rewriter], buffer: buffer, insertion: ins,
|
|
149
|
+
strategy: options[:strategy], config: options[:config]
|
|
150
|
+
)
|
|
151
|
+
end
|
|
105
152
|
|
|
106
|
-
|
|
107
|
-
all = deduplicate_insertions(all, method_overrides_by_pos: method_overrides_by_pos)
|
|
108
|
-
rewriter = Parser::Source::TreeRewriter.new(buffer)
|
|
109
|
-
merge_inserts = Hash.new { |h, k| h[k] = [] }
|
|
110
|
-
changes = []
|
|
111
|
-
|
|
112
|
-
all.sort_by { |(kind, ins)| plugin_insertion_pos(kind, ins) }
|
|
113
|
-
.reverse_each do |kind, ins|
|
|
114
|
-
case kind
|
|
115
|
-
when :method
|
|
116
|
-
pos = plugin_insertion_pos(:method, ins)
|
|
117
|
-
method_override = method_overrides_by_pos[pos]
|
|
118
|
-
|
|
119
|
-
apply_method_insertion!(
|
|
120
|
-
rewriter: rewriter,
|
|
121
|
-
buffer: buffer,
|
|
122
|
-
insertion: ins,
|
|
123
|
-
config: config,
|
|
124
|
-
signature_provider: signature_provider,
|
|
125
|
-
core_rbs_provider: core_rbs_provider,
|
|
126
|
-
strategy: strategy,
|
|
127
|
-
changes: changes,
|
|
128
|
-
file: file.to_s,
|
|
129
|
-
method_override: method_override
|
|
130
|
-
)
|
|
131
|
-
when :attr
|
|
132
|
-
apply_attr_insertion!(
|
|
133
|
-
rewriter: rewriter,
|
|
134
|
-
buffer: buffer,
|
|
135
|
-
insertion: ins,
|
|
136
|
-
config: config,
|
|
137
|
-
signature_provider: signature_provider,
|
|
138
|
-
strategy: strategy,
|
|
139
|
-
merge_inserts: merge_inserts
|
|
140
|
-
)
|
|
141
|
-
when :plugin
|
|
142
|
-
apply_plugin_insertion!(
|
|
143
|
-
rewriter: rewriter,
|
|
144
|
-
buffer: buffer,
|
|
145
|
-
insertion: ins,
|
|
146
|
-
strategy: strategy,
|
|
147
|
-
config: config
|
|
148
|
-
)
|
|
149
|
-
end
|
|
150
|
-
end
|
|
153
|
+
private
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
# Setup rewrite env
|
|
156
|
+
#
|
|
157
|
+
# @private
|
|
158
|
+
# @param [String] code the Ruby source code string to parse and rewrite
|
|
159
|
+
# @param [Hash<Symbol, Object>] options hash containing :config, :file, and :core_rbs_provider
|
|
160
|
+
# @raise [Docscribe::ParseError]
|
|
161
|
+
# @return [Hash<Symbol, Docscribe::Config, String, Parser::Source::Buffer, Parser::AST::Node, Docscribe::Types::ProviderChain, nil, Object, nil>]
|
|
162
|
+
def setup_rewrite_env(code, options)
|
|
163
|
+
config = options[:config] || Docscribe::Config.load
|
|
164
|
+
file = (options[:file] || '(inline)').to_s
|
|
165
|
+
core_rbs_provider = options[:core_rbs_provider]
|
|
166
|
+
buffer = Parser::Source::Buffer.new(file, source: code)
|
|
167
|
+
ast = Docscribe::Parsing.parse_buffer(buffer)
|
|
168
|
+
raise Docscribe::ParseError, "Failed to parse #{file}" unless ast
|
|
153
169
|
|
|
154
|
-
{
|
|
170
|
+
{ config: config, file: file, buffer: buffer, ast: ast,
|
|
171
|
+
signature_provider: build_signature_provider(config, code, file),
|
|
172
|
+
core_rbs_provider: load_core_rbs_provider(config, core_rbs_provider) }
|
|
155
173
|
end
|
|
156
174
|
|
|
157
|
-
|
|
175
|
+
# Load core rbs provider
|
|
176
|
+
#
|
|
177
|
+
# @private
|
|
178
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
179
|
+
# @param [Object, nil] core_rbs_provider optional externally-provided core RBS provider
|
|
180
|
+
# @raise [StandardError]
|
|
181
|
+
# @return [Object, nil] if StandardError
|
|
182
|
+
# @return [nil] if StandardError
|
|
183
|
+
def load_core_rbs_provider(config, core_rbs_provider)
|
|
184
|
+
core_rbs_provider || (config.respond_to?(:core_rbs_provider) ? config.core_rbs_provider : nil)
|
|
185
|
+
rescue StandardError => e
|
|
186
|
+
warn "Docscribe: failed to load core RBS provider: #{e.message}" if ENV.fetch('DOCSCRIBE_DEBUG', false)
|
|
187
|
+
nil
|
|
188
|
+
end
|
|
158
189
|
|
|
159
|
-
#
|
|
190
|
+
# Collect insertions
|
|
160
191
|
#
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
192
|
+
# @private
|
|
193
|
+
# @param [Parser::Source::Buffer] buffer the source buffer to collect insertions from
|
|
194
|
+
# @param [Parser::AST::Node] ast the parsed AST to traverse for collection
|
|
195
|
+
# @return [Array<Object>]
|
|
196
|
+
def collect_insertions(buffer, ast)
|
|
197
|
+
collector = Docscribe::InlineRewriter::Collector.new(buffer)
|
|
198
|
+
collector.process(ast)
|
|
199
|
+
plugin_insertions = Docscribe::Plugin.run_collector_plugins(ast, buffer)
|
|
200
|
+
method_insertions = collector.insertions
|
|
201
|
+
attr_insertions = collector.respond_to?(:attr_insertions) ? collector.attr_insertions : [] #: Array[untyped]
|
|
202
|
+
method_insertions.map { |i| [:method, i] } +
|
|
203
|
+
attr_insertions.map { |i| [:attr, i] } +
|
|
204
|
+
plugin_insertions.map { |i| [:plugin, i] }
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Deduplicate insertions
|
|
168
208
|
#
|
|
169
209
|
# @private
|
|
170
|
-
# @param [Array<
|
|
171
|
-
# @param [nil] method_overrides_by_pos
|
|
172
|
-
#
|
|
210
|
+
# @param [Array<(Symbol, Object)>] insertions insertions to deduplicate
|
|
211
|
+
# @param [Hash<Integer, Hash<Symbol, Object>>, nil?] method_overrides_by_pos method-level overrides keyed
|
|
212
|
+
# by insertion position
|
|
213
|
+
# @return [Array<(Symbol, Object)>]
|
|
173
214
|
def deduplicate_insertions(insertions, method_overrides_by_pos: nil)
|
|
174
|
-
|
|
215
|
+
group_by_position(insertions).each_with_object([]) do |(pos, items), result|
|
|
216
|
+
process_dedup_group(pos, items, result, method_overrides_by_pos)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Process dedup group
|
|
221
|
+
#
|
|
222
|
+
# @private
|
|
223
|
+
# @param [Integer] pos the source begin_pos for the group
|
|
224
|
+
# @param [Array<(Symbol, Object)>] items grouped items to process
|
|
225
|
+
# @param [Array<(Symbol, Object)>] result accumulated result array
|
|
226
|
+
# @param [Hash<Integer, Hash<Symbol, Object>>, nil] method_overrides_by_pos hash mapping position to method
|
|
227
|
+
# override data
|
|
228
|
+
# @return [void]
|
|
229
|
+
def process_dedup_group(pos, items, result, method_overrides_by_pos)
|
|
230
|
+
plugin_items = items.select { |pair| pair.first == :plugin }
|
|
231
|
+
return result.concat(items) if plugin_items.empty?
|
|
232
|
+
|
|
233
|
+
method_items = items.select { |pair| pair.first == :method }
|
|
234
|
+
override_items = find_override_items(plugin_items)
|
|
235
|
+
if override_items.any? && method_items.any?
|
|
236
|
+
handle_override_case(result, items, override_items, method_overrides_by_pos, pos)
|
|
237
|
+
else
|
|
238
|
+
result.concat(deduplicate_items(items, plugin_items, pos, method_items))
|
|
239
|
+
end
|
|
240
|
+
end
|
|
175
241
|
|
|
242
|
+
# Group by position
|
|
243
|
+
#
|
|
244
|
+
# @private
|
|
245
|
+
# @param [Array<(Symbol, Object)>] insertions insertions to group
|
|
246
|
+
# @return [Hash<Integer, Array<(Symbol, Object)>>]
|
|
247
|
+
def group_by_position(insertions)
|
|
248
|
+
groups = {} #: Hash[Integer, untyped]
|
|
176
249
|
insertions.each do |kind, ins|
|
|
177
250
|
pos = plugin_insertion_pos(kind, ins)
|
|
178
251
|
(groups[pos] ||= []) << [kind, ins]
|
|
179
252
|
end
|
|
253
|
+
groups
|
|
254
|
+
end
|
|
180
255
|
|
|
181
|
-
|
|
256
|
+
# Find override items
|
|
257
|
+
#
|
|
258
|
+
# @private
|
|
259
|
+
# @param [Array<(Symbol, Object)>] plugin_items plugin items to check
|
|
260
|
+
# @return [Array<(Symbol, Object)>]
|
|
261
|
+
def find_override_items(plugin_items)
|
|
262
|
+
plugin_items.select do |_k, ins|
|
|
263
|
+
ins.is_a?(Hash) && ins[:method_override].is_a?(Hash)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
182
266
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
267
|
+
# Handle override case
|
|
268
|
+
#
|
|
269
|
+
# @private
|
|
270
|
+
# @param [Array<(Symbol, Object)>] result accumulated result array
|
|
271
|
+
# @param [Array<(Symbol, Object)>] items all items in group
|
|
272
|
+
# @param [Array<(Symbol, Object)>] override_items override plugin items
|
|
273
|
+
# @param [Hash<Integer, Hash<Symbol, Object>>, nil] method_overrides_by_pos hash mapping position to
|
|
274
|
+
# method override data
|
|
275
|
+
# @param [Integer] pos the source position of the conflict
|
|
276
|
+
# @return [void]
|
|
277
|
+
def handle_override_case(result, items, override_items, method_overrides_by_pos, pos)
|
|
278
|
+
if method_overrides_by_pos
|
|
279
|
+
winning_ins = pick_highest_priority_override_insertion(override_items, pos: pos)
|
|
280
|
+
method_overrides_by_pos[pos] = winning_ins[:method_override] if winning_ins
|
|
281
|
+
end
|
|
186
282
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
next
|
|
191
|
-
end
|
|
283
|
+
items = items.reject { |k, ins| k == :plugin && ins.is_a?(Hash) && ins.key?(:method_override) }
|
|
284
|
+
result.concat(items)
|
|
285
|
+
end
|
|
192
286
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
if Docscribe::Plugin.debug? && dropped.any?
|
|
223
|
-
kept_labels = items.select { |k, _| k == :plugin }
|
|
224
|
-
.map { |_k, ins| plugin_insertion_label(ins) }
|
|
225
|
-
.uniq
|
|
226
|
-
dropped_labels = dropped.map { |_k, ins| plugin_insertion_label(ins) }.uniq
|
|
227
|
-
line = plugin_insertion_line(plugin_items.first[1])
|
|
228
|
-
loc = +"pos=#{pos}"
|
|
229
|
-
loc << " line=#{line}" if line
|
|
230
|
-
warn "Docscribe: CollectorPlugin conflict at #{loc} — " \
|
|
231
|
-
"#{dropped_labels.join(', ')} (pri=#{dropped.map { |_k, ins| plugin_insertion_priority(ins) }.max}) " \
|
|
232
|
-
"dropped in favor of #{kept_labels.join(', ')} (pri=#{max_prio}). " \
|
|
233
|
-
'Set explicit priority or adjust anchor_node to avoid collision.'
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
result.concat(items)
|
|
237
|
-
next
|
|
238
|
-
end
|
|
287
|
+
# Deduplicate items
|
|
288
|
+
#
|
|
289
|
+
# @private
|
|
290
|
+
# @param [Array<(Symbol, Object)>] items all items in group
|
|
291
|
+
# @param [Array<(Symbol, Object)>] plugin_items plugin items in group
|
|
292
|
+
# @param [Integer] pos the source position of the conflict
|
|
293
|
+
# @param [Array<(Symbol, Object)>] _method_items method items in group
|
|
294
|
+
# @return [Array<(Symbol, Object)>]
|
|
295
|
+
def deduplicate_items(items, plugin_items, pos, _method_items)
|
|
296
|
+
plugin_doc_items = plugin_items.select { |pair| plugin_doc_item?(pair) }
|
|
297
|
+
|
|
298
|
+
if plugin_doc_items.any?
|
|
299
|
+
deduplicate_plugin_doc_case(items, plugin_doc_items, pos)
|
|
300
|
+
else
|
|
301
|
+
items.reject { |pair| override_or_plugin_method?(pair) }
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Plugin doc item
|
|
306
|
+
#
|
|
307
|
+
# @private
|
|
308
|
+
# @param [(Symbol, Object)] pair insertion pair to check
|
|
309
|
+
# @return [Boolean]
|
|
310
|
+
def plugin_doc_item?(pair)
|
|
311
|
+
_k, ins = pair
|
|
312
|
+
ins.is_a?(Hash) && ins[:doc] && !ins[:doc].empty?
|
|
313
|
+
end
|
|
239
314
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
315
|
+
# Deduplicate plugin doc case
|
|
316
|
+
#
|
|
317
|
+
# @private
|
|
318
|
+
# @param [Array<(Symbol, Object)>] items all items in group
|
|
319
|
+
# @param [Array<(Symbol, Object)>] plugin_doc_items plugin doc items
|
|
320
|
+
# @param [Integer] pos the source position of the conflict
|
|
321
|
+
# @return [Array<(Symbol, Object)>]
|
|
322
|
+
def deduplicate_plugin_doc_case(items, plugin_doc_items, pos)
|
|
323
|
+
items = items.reject { |k, _| k == :method }
|
|
324
|
+
items = items.reject { |pair| override_or_plugin_method?(pair) }
|
|
246
325
|
|
|
247
|
-
|
|
248
|
-
|
|
326
|
+
max_prio = max_plugin_priority(plugin_doc_items)
|
|
327
|
+
dropped = filter_lower_priority_plugins(items, max_prio)
|
|
328
|
+
items = items.reject { |k, ins| dropped.include?([k, ins]) }
|
|
249
329
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
330
|
+
warn_plugin_conflict!(dropped, plugin_doc_items, max_prio, pos) if Docscribe::Plugin.debug? && dropped.any?
|
|
331
|
+
|
|
332
|
+
items
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Override or plugin method
|
|
336
|
+
#
|
|
337
|
+
# @private
|
|
338
|
+
# @param [(Symbol, Object)] pair insertion pair to check
|
|
339
|
+
# @return [Boolean]
|
|
340
|
+
def override_or_plugin_method?(pair)
|
|
341
|
+
k, ins = pair
|
|
342
|
+
k == :plugin && ins.is_a?(Hash) && ins.key?(:method_override)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Max plugin priority
|
|
346
|
+
#
|
|
347
|
+
# @private
|
|
348
|
+
# @param [Array<(Symbol, Object)>] plugin_items plugin items to scan
|
|
349
|
+
# @return [Integer]
|
|
350
|
+
def max_plugin_priority(plugin_items)
|
|
351
|
+
plugin_items.map { |_k, ins| plugin_insertion_priority(ins) }.max || 0
|
|
352
|
+
end
|
|
253
353
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
354
|
+
# Filter lower priority plugins
|
|
355
|
+
#
|
|
356
|
+
# @private
|
|
357
|
+
# @param [Array<(Symbol, Object)>] items items to filter
|
|
358
|
+
# @param [Integer] threshold minimum priority threshold
|
|
359
|
+
# @return [Array<(Symbol, Object)>]
|
|
360
|
+
def filter_lower_priority_plugins(items, threshold)
|
|
361
|
+
items.select do |k, ins|
|
|
362
|
+
k == :plugin && ins.is_a?(Hash) && ins[:doc] && plugin_insertion_priority(ins) < threshold
|
|
257
363
|
end
|
|
364
|
+
end
|
|
258
365
|
|
|
259
|
-
|
|
366
|
+
# Warn plugin conflict
|
|
367
|
+
#
|
|
368
|
+
# @private
|
|
369
|
+
# @param [Array<(Symbol, Object)>] dropped dropped plugin items
|
|
370
|
+
# @param [Array<(Symbol, Object)>] plugin_items kept plugin items
|
|
371
|
+
# @param [Integer] max_prio the maximum priority value
|
|
372
|
+
# @param [Integer] pos the source position of the conflict
|
|
373
|
+
# @return [void]
|
|
374
|
+
def warn_plugin_conflict!(dropped, plugin_items, max_prio, pos)
|
|
375
|
+
kept_labels = plugin_items.map { |_k, ins| plugin_insertion_label(ins) }.uniq
|
|
376
|
+
dropped_labels = dropped.map { |_k, ins| plugin_insertion_label(ins) }.uniq
|
|
377
|
+
loc = conflict_location_str(pos, plugin_items)
|
|
378
|
+
warn "Docscribe: CollectorPlugin conflict at #{loc} — " \
|
|
379
|
+
"#{dropped_labels.join(', ')} (pri=#{dropped.map { |_k, ins| plugin_insertion_priority(ins) }.max}) " \
|
|
380
|
+
"dropped in favor of #{kept_labels.join(', ')} (pri=#{max_prio}). " \
|
|
381
|
+
'Set explicit priority or adjust anchor_node to avoid collision.'
|
|
260
382
|
end
|
|
261
383
|
|
|
384
|
+
# Conflict location str
|
|
385
|
+
#
|
|
262
386
|
# @private
|
|
263
|
-
# @param
|
|
264
|
-
# @param
|
|
265
|
-
# @return [
|
|
387
|
+
# @param [Integer] pos the source position of the conflict
|
|
388
|
+
# @param [Array<(Symbol, Object)>] plugin_items plugin items for location
|
|
389
|
+
# @return [String]
|
|
390
|
+
def conflict_location_str(pos, plugin_items)
|
|
391
|
+
line = plugin_insertion_line(plugin_items.first[1])
|
|
392
|
+
"pos=#{pos}#{" line=#{line}" if line}"
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Pick highest priority override insertion
|
|
396
|
+
#
|
|
397
|
+
# @private
|
|
398
|
+
# @param [Array<(Symbol, Object)>] override_items override items to prioritize
|
|
399
|
+
# @param [Integer] pos begin_pos (used only for debug output)
|
|
400
|
+
# @return [Hash<Symbol, Object>, nil] winning insertion hash (the one whose override will be applied)
|
|
266
401
|
def pick_highest_priority_override_insertion(override_items, pos:)
|
|
267
402
|
return nil if override_items.empty?
|
|
268
403
|
|
|
269
|
-
max_prio =
|
|
270
|
-
|
|
404
|
+
max_prio = max_plugin_priority_for(override_items)
|
|
405
|
+
winners = override_items.select { |_k, ins| plugin_insertion_priority(ins) == max_prio }
|
|
406
|
+
winners_sorted = sort_winners_by_order(winners)
|
|
271
407
|
|
|
272
|
-
|
|
273
|
-
override_items.select { |_k, ins| plugin_insertion_priority(ins) == max_prio }
|
|
408
|
+
warn_override_conflict!(winners_sorted, max_prio, pos)
|
|
274
409
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
winners_sorted =
|
|
278
|
-
winners.sort_by do |_k, ins|
|
|
279
|
-
order = ins.is_a?(Hash) ? ins[:__docscribe_plugin_order] : nil
|
|
280
|
-
order.nil? ? 0 : order
|
|
281
|
-
end
|
|
410
|
+
winners_sorted.first[1]
|
|
411
|
+
end
|
|
282
412
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
413
|
+
# Max plugin priority for
|
|
414
|
+
#
|
|
415
|
+
# @private
|
|
416
|
+
# @param [Array<(Symbol, Object)>] override_items override items to evaluate
|
|
417
|
+
# @return [Integer]
|
|
418
|
+
def max_plugin_priority_for(override_items)
|
|
419
|
+
override_items.map { |_k, ins| plugin_insertion_priority(ins) }.max || 0
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Sort winners by order
|
|
423
|
+
#
|
|
424
|
+
# @private
|
|
425
|
+
# @param [Array<(Symbol, Object)>] winners winning items to sort
|
|
426
|
+
# @return [Array<(Symbol, Object)>]
|
|
427
|
+
def sort_winners_by_order(winners)
|
|
428
|
+
winners.sort_by do |_k, ins|
|
|
429
|
+
order = ins.is_a?(Hash) ? ins[:__docscribe_plugin_order] : nil
|
|
430
|
+
order || 0
|
|
292
431
|
end
|
|
432
|
+
end
|
|
293
433
|
|
|
294
|
-
|
|
434
|
+
# Warn override conflict
|
|
435
|
+
#
|
|
436
|
+
# @private
|
|
437
|
+
# @param [Array<(Symbol, Object)>] winners_sorted sorted winning items
|
|
438
|
+
# @param [Integer] max_prio the maximum priority value
|
|
439
|
+
# @param [Integer] pos the source position of the conflict
|
|
440
|
+
# @return [void]
|
|
441
|
+
def warn_override_conflict!(winners_sorted, max_prio, pos)
|
|
442
|
+
return unless Docscribe::Plugin.debug?
|
|
443
|
+
|
|
444
|
+
labels = winners_sorted.map { |_k, ins| plugin_insertion_label(ins) }.uniq
|
|
445
|
+
return unless labels.size > 1
|
|
446
|
+
|
|
447
|
+
line = plugin_insertion_line(winners_sorted.first[1])
|
|
448
|
+
loc = +"pos=#{pos}"
|
|
449
|
+
loc << " line=#{line}" if line
|
|
450
|
+
warn "Docscribe: method_override conflict at #{loc} (priority=#{max_prio}): " \
|
|
451
|
+
"#{labels.join(', ')} — using first by registration order."
|
|
295
452
|
end
|
|
296
453
|
|
|
454
|
+
# Plugin insertion priority
|
|
455
|
+
#
|
|
297
456
|
# @private
|
|
298
|
-
# @param [Hash] insertion
|
|
457
|
+
# @param [Hash<Symbol, Object>, Docscribe::InlineRewriter::Collector::Insertion, Docscribe::InlineRewriter::Collector::AttrInsertion] insertion the collected method insertion
|
|
299
458
|
# @raise [StandardError]
|
|
300
|
-
# @return [Integer]
|
|
459
|
+
# @return [Integer] if StandardError
|
|
460
|
+
# @return [Integer] if StandardError
|
|
301
461
|
def plugin_insertion_priority(insertion)
|
|
302
462
|
return 0 unless insertion.is_a?(Hash)
|
|
303
463
|
|
|
@@ -306,10 +466,13 @@ module Docscribe
|
|
|
306
466
|
0
|
|
307
467
|
end
|
|
308
468
|
|
|
469
|
+
# Plugin insertion label
|
|
470
|
+
#
|
|
309
471
|
# @private
|
|
310
|
-
# @param [Hash] insertion
|
|
472
|
+
# @param [Hash<Symbol, Object>, Docscribe::InlineRewriter::Collector::Insertion, Docscribe::InlineRewriter::Collector::AttrInsertion] insertion the collected method insertion
|
|
311
473
|
# @raise [StandardError]
|
|
312
|
-
# @return [String]
|
|
474
|
+
# @return [String] if StandardError
|
|
475
|
+
# @return [String] if StandardError
|
|
313
476
|
def plugin_insertion_label(insertion)
|
|
314
477
|
return 'unknown' unless insertion.is_a?(Hash)
|
|
315
478
|
|
|
@@ -319,63 +482,74 @@ module Docscribe
|
|
|
319
482
|
'unknown'
|
|
320
483
|
end
|
|
321
484
|
|
|
485
|
+
# Plugin insertion line
|
|
486
|
+
#
|
|
322
487
|
# @private
|
|
323
|
-
# @param [Hash] insertion
|
|
488
|
+
# @param [Hash<Symbol, Object>, Docscribe::InlineRewriter::Collector::Insertion, Docscribe::InlineRewriter::Collector::AttrInsertion] insertion the collected method insertion
|
|
324
489
|
# @raise [StandardError]
|
|
325
|
-
# @return [Integer, nil]
|
|
490
|
+
# @return [Integer, nil] if StandardError
|
|
491
|
+
# @return [nil] if StandardError
|
|
326
492
|
def plugin_insertion_line(insertion)
|
|
327
493
|
return nil unless insertion.is_a?(Hash)
|
|
328
494
|
|
|
329
|
-
insertion[:anchor_node]
|
|
495
|
+
anchor_node = insertion[:anchor_node]
|
|
496
|
+
expression = anchor_node&.loc&.expression
|
|
497
|
+
expression&.line
|
|
330
498
|
rescue StandardError
|
|
331
499
|
nil
|
|
332
500
|
end
|
|
333
501
|
|
|
334
|
-
#
|
|
335
|
-
# insertions (method/attr) and Hash-based insertions (plugin).
|
|
502
|
+
# Plugin insertion pos
|
|
336
503
|
#
|
|
337
504
|
# @private
|
|
338
505
|
# @param [Symbol] kind :method, :attr, or :plugin
|
|
339
|
-
# @param [Object] ins insertion
|
|
506
|
+
# @param [Hash<Symbol, Object>] ins insertion to locate
|
|
340
507
|
# @return [Integer]
|
|
341
508
|
def plugin_insertion_pos(kind, ins)
|
|
342
509
|
case kind
|
|
343
510
|
when :plugin
|
|
344
|
-
ins[
|
|
511
|
+
plugin_ins = ins #: Hash[Symbol, untyped]
|
|
512
|
+
plugin_ins[:anchor_node].loc.expression.begin_pos
|
|
345
513
|
else
|
|
346
|
-
ins
|
|
514
|
+
method_ins = ins #: Collector::Insertion | Collector::AttrInsertion
|
|
515
|
+
method_ins.node.loc.expression.begin_pos
|
|
347
516
|
end
|
|
348
517
|
end
|
|
349
518
|
|
|
350
|
-
# Apply
|
|
351
|
-
#
|
|
352
|
-
# :safe — skip if a doc-like block already exists above anchor_node
|
|
353
|
-
# :aggressive — remove existing doc block, insert fresh
|
|
519
|
+
# Apply plugin insertion
|
|
354
520
|
#
|
|
355
521
|
# @private
|
|
356
|
-
# @param [Parser::Source::TreeRewriter] rewriter
|
|
357
|
-
# @param [Parser::Source::Buffer] buffer
|
|
358
|
-
# @param [Hash] insertion { anchor_node:, doc: }
|
|
359
|
-
# @param [Symbol] strategy
|
|
360
|
-
# @param [Docscribe::Config] config
|
|
522
|
+
# @param [Parser::Source::TreeRewriter] rewriter the TreeRewriter accumulating source transformations
|
|
523
|
+
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
524
|
+
# @param [Hash<Symbol, Object>] insertion { anchor_node:, doc: }
|
|
525
|
+
# @param [Symbol] strategy :safe or :aggressive rewrite mode
|
|
526
|
+
# @param [Docscribe::Config] config the active configuration
|
|
361
527
|
# @return [void]
|
|
362
528
|
def apply_plugin_insertion!(rewriter:, buffer:, insertion:, strategy:, config:)
|
|
363
|
-
anchor_node = insertion
|
|
364
|
-
doc = insertion[:doc]
|
|
529
|
+
anchor_node, doc = insertion.values_at(:anchor_node, :doc)
|
|
365
530
|
return unless anchor_node && doc && !doc.empty?
|
|
366
531
|
|
|
367
532
|
indent = SourceHelpers.line_indent(anchor_node)
|
|
368
|
-
doc
|
|
533
|
+
doc = normalize_plugin_doc(doc, indent, config: config, anchor_node: anchor_node)
|
|
369
534
|
bol_range = SourceHelpers.line_start_range(buffer, anchor_node)
|
|
535
|
+
insert_plugin_doc(rewriter, buffer, bol_range, doc, strategy)
|
|
536
|
+
end
|
|
370
537
|
|
|
538
|
+
# Insert plugin doc
|
|
539
|
+
#
|
|
540
|
+
# @private
|
|
541
|
+
# @param [Parser::Source::TreeRewriter] rewriter the TreeRewriter accumulating source transformations
|
|
542
|
+
# @param [Parser::Source::Buffer] buffer the source buffer being rewritten
|
|
543
|
+
# @param [Parser::Source::Range] bol_range the beginning-of-line range for the anchor node
|
|
544
|
+
# @param [String] doc the normalized documentation string to insert
|
|
545
|
+
# @param [Symbol] strategy :safe or :aggressive rewrite mode
|
|
546
|
+
# @return [void]
|
|
547
|
+
def insert_plugin_doc(rewriter, buffer, bol_range, doc, strategy)
|
|
371
548
|
case strategy
|
|
372
549
|
when :aggressive
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
rewriter.remove(range)
|
|
376
|
-
end
|
|
550
|
+
range = any_comment_block_removal_range(buffer, bol_range.begin_pos)
|
|
551
|
+
rewriter.remove(range) if range
|
|
377
552
|
rewriter.insert_before(bol_range, doc)
|
|
378
|
-
|
|
379
553
|
when :safe
|
|
380
554
|
return if SourceHelpers.already_has_doc_immediately_above?(buffer, bol_range.begin_pos)
|
|
381
555
|
|
|
@@ -383,95 +557,130 @@ module Docscribe
|
|
|
383
557
|
end
|
|
384
558
|
end
|
|
385
559
|
|
|
386
|
-
#
|
|
387
|
-
# regardless of whether it looks like documentation.
|
|
388
|
-
#
|
|
389
|
-
# Used by CollectorPlugin in aggressive mode where the plugin itself
|
|
390
|
-
# is responsible for deciding what to replace.
|
|
560
|
+
# Any comment block removal range
|
|
391
561
|
#
|
|
392
562
|
# @private
|
|
393
|
-
# @param [Parser::Source::Buffer] buffer
|
|
563
|
+
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
394
564
|
# @param [Integer] bol_pos beginning-of-line position of anchor_node
|
|
395
565
|
# @return [Parser::Source::Range, nil]
|
|
396
566
|
def any_comment_block_removal_range(buffer, bol_pos)
|
|
397
567
|
src = buffer.source
|
|
398
568
|
lines = src.lines
|
|
399
|
-
|
|
400
|
-
|
|
569
|
+
i = nearest_comment_line_index(src, lines, bol_pos)
|
|
570
|
+
return nil unless i
|
|
401
571
|
|
|
402
|
-
|
|
403
|
-
i -= 1 while i >= 0 && lines[i].strip.empty?
|
|
572
|
+
start_idx = comment_block_start_index(lines, i)
|
|
404
573
|
|
|
405
|
-
|
|
406
|
-
return nil
|
|
574
|
+
removable_start_idx = skip_preserved_lines(lines, start_idx, i)
|
|
575
|
+
return nil if removable_start_idx > i
|
|
407
576
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
start_idx += 1
|
|
577
|
+
start_pos = removable_start_idx.positive? ? (lines[0...removable_start_idx] || []).join.length : 0
|
|
578
|
+
Parser::Source::Range.new(buffer, start_pos, bol_pos)
|
|
579
|
+
end
|
|
412
580
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
581
|
+
# Nearest comment line index
|
|
582
|
+
#
|
|
583
|
+
# @private
|
|
584
|
+
# @param [String] src the full source string of the buffer
|
|
585
|
+
# @param [Array<String>] lines array of source code lines
|
|
586
|
+
# @param [Integer] bol_pos character position of the beginning of the anchor line
|
|
587
|
+
# @return [Integer, nil]
|
|
588
|
+
def nearest_comment_line_index(src, lines, bol_pos)
|
|
589
|
+
def_line_idx = (src[0...bol_pos] || '').count("\n")
|
|
590
|
+
i = def_line_idx - 1
|
|
591
|
+
i -= 1 while i >= 0 && lines[i].strip.empty?
|
|
592
|
+
return nil unless i >= 0 && lines[i] =~ /^\s*#/
|
|
419
593
|
|
|
420
|
-
|
|
594
|
+
i
|
|
595
|
+
end
|
|
421
596
|
|
|
422
|
-
|
|
423
|
-
|
|
597
|
+
# Comment block start index
|
|
598
|
+
#
|
|
599
|
+
# @private
|
|
600
|
+
# @param [Array<String>] lines array of source code lines
|
|
601
|
+
# @param [Integer] def_line_idx the index in lines of the method definition (anchor) line
|
|
602
|
+
# @return [Integer]
|
|
603
|
+
def comment_block_start_index(lines, def_line_idx)
|
|
604
|
+
start_idx = def_line_idx
|
|
605
|
+
start_idx -= 1 while start_idx >= 0 && lines[start_idx] =~ /^\s*#/
|
|
606
|
+
start_idx + 1
|
|
424
607
|
end
|
|
425
608
|
|
|
426
|
-
#
|
|
609
|
+
# Skip preserved lines
|
|
427
610
|
#
|
|
428
|
-
#
|
|
429
|
-
#
|
|
430
|
-
#
|
|
431
|
-
#
|
|
432
|
-
#
|
|
611
|
+
# @private
|
|
612
|
+
# @param [Array<String>] lines array of source code lines
|
|
613
|
+
# @param [Integer] start_idx index of the first line of the comment block
|
|
614
|
+
# @param [Integer] def_line_idx the index in lines of the method definition (anchor) line
|
|
615
|
+
# @return [Integer]
|
|
616
|
+
def skip_preserved_lines(lines, start_idx, def_line_idx)
|
|
617
|
+
idx = start_idx
|
|
618
|
+
idx += 1 while idx <= def_line_idx && SourceHelpers.preserved_comment_line?(lines[idx])
|
|
619
|
+
idx
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
# Normalize plugin doc
|
|
433
623
|
#
|
|
434
624
|
# @private
|
|
435
|
-
# @param
|
|
436
|
-
# @param
|
|
437
|
-
# @param
|
|
438
|
-
# @param
|
|
625
|
+
# @param [String] doc Raw doc string returned by a CollectorPlugin insertion (`:doc`)
|
|
626
|
+
# @param [String] indent Indentation to apply to every doc line
|
|
627
|
+
# @param [Docscribe::Config] config Effective Docscribe config for this run
|
|
628
|
+
# @param [Parser::AST::Node, nil] anchor_node AST node used as insertion anchor
|
|
439
629
|
# @return [String] Normalized doc string ready to be inserted
|
|
440
630
|
def normalize_plugin_doc(doc, indent, config:, anchor_node:)
|
|
441
631
|
doc = normalize_plugin_doc_indent(doc, indent)
|
|
632
|
+
doc = trim_trailing_blank_lines(doc)
|
|
633
|
+
if anchor_node && %i[def defs].include?(anchor_node.type) && config.include_default_message?
|
|
634
|
+
doc = prepend_default_message_if_no_prose(doc, anchor_node, indent, config)
|
|
635
|
+
end
|
|
636
|
+
doc
|
|
637
|
+
end
|
|
442
638
|
|
|
639
|
+
# Trim trailing blank lines
|
|
640
|
+
#
|
|
641
|
+
# @private
|
|
642
|
+
# @param [String] doc the documentation string to trim
|
|
643
|
+
# @return [String]
|
|
644
|
+
def trim_trailing_blank_lines(doc)
|
|
443
645
|
lines = doc.lines
|
|
444
646
|
lines.pop while lines.any? && lines.last.strip.empty?
|
|
647
|
+
result = lines.join
|
|
648
|
+
result.end_with?("\n") ? result : "#{result}\n"
|
|
649
|
+
end
|
|
445
650
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
next false if s.start_with?('# @') # tag line
|
|
457
|
-
next false if s.start_with?('# +') # header line
|
|
651
|
+
# Prepend default message if no prose
|
|
652
|
+
#
|
|
653
|
+
# @private
|
|
654
|
+
# @param [String] doc the plugin-generated documentation string
|
|
655
|
+
# @param [Parser::AST::Node] anchor_node the AST node used as the insertion anchor
|
|
656
|
+
# @param [String] indent whitespace indentation prefix derived from the anchor node
|
|
657
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
658
|
+
# @return [String]
|
|
659
|
+
def prepend_default_message_if_no_prose(doc, anchor_node, indent, config)
|
|
660
|
+
return doc if doc_has_prose?(doc)
|
|
458
661
|
|
|
459
|
-
|
|
460
|
-
|
|
662
|
+
scope = anchor_node.type == :defs ? :class : :instance
|
|
663
|
+
msg = config.default_message(scope, :public)
|
|
664
|
+
"#{indent}# #{msg}\n#{indent}#\n" + doc
|
|
665
|
+
end
|
|
461
666
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
667
|
+
# Doc has prose
|
|
668
|
+
#
|
|
669
|
+
# @private
|
|
670
|
+
# @param [String] doc the documentation string to inspect
|
|
671
|
+
# @return [Boolean]
|
|
672
|
+
def doc_has_prose?(doc)
|
|
673
|
+
doc.lines.any? do |l|
|
|
674
|
+
s = l.strip
|
|
675
|
+
next false if s.empty? || s == '#'
|
|
676
|
+
next false if s.start_with?('# @')
|
|
677
|
+
next false if s.start_with?('# +')
|
|
678
|
+
|
|
679
|
+
true
|
|
465
680
|
end
|
|
466
|
-
|
|
467
|
-
doc
|
|
468
681
|
end
|
|
469
682
|
|
|
470
|
-
# Normalize
|
|
471
|
-
#
|
|
472
|
-
# Plugins produce doc strings without knowledge of the surrounding
|
|
473
|
-
# indentation. We strip leading whitespace from each non-empty line
|
|
474
|
-
# and re-prefix it with the indent derived from anchor_node.
|
|
683
|
+
# Normalize plugin doc indent
|
|
475
684
|
#
|
|
476
685
|
# @private
|
|
477
686
|
# @param [String] doc raw doc string from plugin
|
|
@@ -484,18 +693,12 @@ module Docscribe
|
|
|
484
693
|
end.join
|
|
485
694
|
end
|
|
486
695
|
|
|
487
|
-
# Normalize strategy
|
|
488
|
-
#
|
|
489
|
-
# Precedence:
|
|
490
|
-
# - explicit `strategy`
|
|
491
|
-
# - `rewrite: true` => `:aggressive`
|
|
492
|
-
# - `merge: true` => `:safe`
|
|
493
|
-
# - default => `:safe`
|
|
696
|
+
# Normalize strategy
|
|
494
697
|
#
|
|
495
698
|
# @private
|
|
496
|
-
# @param [Symbol, nil] strategy
|
|
497
|
-
# @param [Boolean, nil] rewrite
|
|
498
|
-
# @param [Boolean, nil] merge
|
|
699
|
+
# @param [Symbol, nil] strategy :safe or :aggressive rewrite mode
|
|
700
|
+
# @param [Boolean, nil] rewrite compatibility alias for aggressive strategy
|
|
701
|
+
# @param [Boolean, nil] merge compatibility alias for safe strategy
|
|
499
702
|
# @return [Symbol]
|
|
500
703
|
def normalize_strategy(strategy:, rewrite:, merge:)
|
|
501
704
|
return strategy if strategy
|
|
@@ -505,10 +708,10 @@ module Docscribe
|
|
|
505
708
|
:safe
|
|
506
709
|
end
|
|
507
710
|
|
|
508
|
-
# Validate
|
|
711
|
+
# Validate strategy
|
|
509
712
|
#
|
|
510
713
|
# @private
|
|
511
|
-
# @param [Symbol] strategy
|
|
714
|
+
# @param [Symbol] strategy :safe or :aggressive rewrite mode
|
|
512
715
|
# @raise [ArgumentError]
|
|
513
716
|
# @return [void]
|
|
514
717
|
def validate_strategy!(strategy)
|
|
@@ -517,474 +720,711 @@ module Docscribe
|
|
|
517
720
|
raise ArgumentError, "Unknown strategy: #{strategy.inspect}"
|
|
518
721
|
end
|
|
519
722
|
|
|
520
|
-
# Apply
|
|
723
|
+
# Apply method insertion
|
|
521
724
|
#
|
|
522
|
-
#
|
|
523
|
-
#
|
|
524
|
-
#
|
|
725
|
+
# @private
|
|
726
|
+
# @param [Object] options kwargs with insertion, config, rewriter, buffer, strategy, changes, file, doc params
|
|
727
|
+
# @return [void]
|
|
728
|
+
def apply_method_insertion!(**options)
|
|
729
|
+
insertion = options[:insertion]
|
|
730
|
+
config = options[:config]
|
|
731
|
+
return unless method_insertion_allowed?(insertion, config)
|
|
732
|
+
|
|
733
|
+
anchor_bol_range, = method_bol_ranges(options[:buffer], insertion)
|
|
734
|
+
params = build_method_insertion_params(insertion, config, options[:signature_provider],
|
|
735
|
+
options[:core_rbs_provider], options[:method_override])
|
|
736
|
+
extract_existing_descriptions!(options[:buffer], insertion, params, options[:strategy], config)
|
|
737
|
+
doc = DocBuilder.build(insertion, **params) # steep:ignore
|
|
738
|
+
dispatch_method_insertion_by_strategy!(anchor_bol_range, options, params, doc)
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
# Dispatch method insertion by strategy
|
|
742
|
+
#
|
|
743
|
+
# @private
|
|
744
|
+
# @param [Parser::Source::Range] anchor_bol_range the beginning-of-line range for the anchor node
|
|
745
|
+
# @param [Hash<Symbol, Object>] options the full keyword options hash passed to apply_method_insertion!
|
|
746
|
+
# @param [Hash<Symbol, Object>] params precomputed insertion parameters (types, overrides, config)
|
|
747
|
+
# @param [String, nil] doc the generated documentation block string
|
|
748
|
+
# @return [void]
|
|
749
|
+
def dispatch_method_insertion_by_strategy!(anchor_bol_range, options, params, doc)
|
|
750
|
+
base = { anchor_bol_range: anchor_bol_range, insertion: options[:insertion],
|
|
751
|
+
rewriter: options[:rewriter], buffer: options[:buffer],
|
|
752
|
+
changes: options[:changes], file: options[:file] }
|
|
753
|
+
case options[:strategy]
|
|
754
|
+
when :aggressive then apply_method_insertion_aggressive!(**base, doc: doc)
|
|
755
|
+
when :safe then apply_method_insertion_safe!(**base, strategy: options[:strategy], **params)
|
|
756
|
+
end
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
# Method insertion allowed
|
|
525
760
|
#
|
|
526
|
-
#
|
|
527
|
-
#
|
|
528
|
-
#
|
|
761
|
+
# @private
|
|
762
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
763
|
+
# @param [Docscribe::Config] config the active configuration
|
|
764
|
+
# @return [Boolean] true if insertion should proceed
|
|
765
|
+
def method_insertion_allowed?(insertion, config)
|
|
766
|
+
name = SourceHelpers.node_name(insertion.node) #: Symbol
|
|
767
|
+
config.process_method?(container: insertion.container, scope: insertion.scope,
|
|
768
|
+
visibility: insertion.visibility || :public, name: name)
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
# Extract existing descriptions
|
|
529
772
|
#
|
|
530
773
|
# @private
|
|
531
|
-
# @param [Parser::Source::
|
|
532
|
-
# @param [
|
|
533
|
-
# @param [
|
|
534
|
-
# @param [
|
|
535
|
-
# @param [
|
|
536
|
-
# @param [Symbol] strategy
|
|
537
|
-
# @param [Array<Hash>] changes structured change records
|
|
538
|
-
# @param [String] file
|
|
539
|
-
# @param [Object] core_rbs_provider Param documentation.
|
|
540
|
-
# @param [nil] method_override Param documentation.
|
|
774
|
+
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
775
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
776
|
+
# @param [Hash<Symbol, Object>] params precomputed attribute insertion parameters
|
|
777
|
+
# @param [Symbol] strategy :safe or :aggressive rewrite mode
|
|
778
|
+
# @param [Docscribe::Config] config the active configuration
|
|
541
779
|
# @return [void]
|
|
542
|
-
def
|
|
543
|
-
|
|
544
|
-
name = SourceHelpers.node_name(insertion.node)
|
|
780
|
+
def extract_existing_descriptions!(buffer, insertion, params, strategy, config)
|
|
781
|
+
return unless strategy == :aggressive && config.keep_descriptions?
|
|
545
782
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
scope: insertion.scope,
|
|
549
|
-
visibility: insertion.visibility,
|
|
550
|
-
name: name
|
|
783
|
+
parsed = DocBuilder.parse_existing_doc_tags(
|
|
784
|
+
method_doc_comment_info(buffer, insertion)&.dig(:doc_lines) || []
|
|
551
785
|
)
|
|
786
|
+
merge_existing_descriptions!(params, parsed)
|
|
787
|
+
end
|
|
552
788
|
|
|
553
|
-
|
|
789
|
+
# Merge parsed descriptions into insertion params
|
|
790
|
+
#
|
|
791
|
+
# @private
|
|
792
|
+
# @param [Hash<Symbol, Object>] params insertion params
|
|
793
|
+
# @param [Hash<Symbol, Object>] parsed parsed tag info
|
|
794
|
+
# @return [void]
|
|
795
|
+
def merge_existing_descriptions!(params, parsed)
|
|
796
|
+
params[:param_descriptions] = parsed[:param_descriptions] if parsed[:param_descriptions].any?
|
|
797
|
+
params[:return_description] = parsed[:return_description] if parsed[:return_description]
|
|
798
|
+
params[:description] = parsed[:description] if parsed[:description].any?
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
# Build method insertion params
|
|
802
|
+
#
|
|
803
|
+
# @private
|
|
804
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
805
|
+
# @param [Docscribe::Config] config the active configuration
|
|
806
|
+
# @param [Docscribe::Types::ProviderChain, nil] signature_provider RBS signature provider
|
|
807
|
+
# @param [Object, nil] core_rbs_provider optional externally-provided core RBS provider
|
|
808
|
+
# @param [Hash<Symbol, Object>, nil] method_override the raw override data
|
|
809
|
+
# @return [Hash<Symbol, Object>]
|
|
810
|
+
def build_method_insertion_params(insertion, config, signature_provider, core_rbs_provider, method_override)
|
|
811
|
+
override = extract_method_override!(method_override)
|
|
812
|
+
effective = build_effective_params(insertion, config: config, signature_provider: signature_provider,
|
|
813
|
+
core_rbs_provider: core_rbs_provider, override: override)
|
|
814
|
+
{ **effective, config: config, signature_provider: signature_provider,
|
|
815
|
+
core_rbs_provider: core_rbs_provider }
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
# Build effective params
|
|
819
|
+
#
|
|
820
|
+
# @private
|
|
821
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
822
|
+
# @param [Object] options keyword options
|
|
823
|
+
# @return [Hash<Symbol, Hash<String, String>, nil, String, nil, Array<Docscribe::Plugin::Tag>>]
|
|
824
|
+
def build_effective_params(insertion, **options)
|
|
825
|
+
external_sig = resolve_external_signature(insertion, options[:signature_provider])
|
|
826
|
+
param_types = resolve_param_types(insertion, external_sig, options[:config])
|
|
827
|
+
override = options[:override]
|
|
828
|
+
|
|
829
|
+
param_types = (param_types || {}).merge(override[:param_types]) if override[:param_types]&.any?
|
|
830
|
+
|
|
831
|
+
{ param_types: param_types, return_type_override: override[:return_type], override_tags: override[:tags] }
|
|
832
|
+
end
|
|
554
833
|
|
|
555
|
-
|
|
556
|
-
|
|
834
|
+
# Resolve external signature
|
|
835
|
+
#
|
|
836
|
+
# @private
|
|
837
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
838
|
+
# @param [Docscribe::Types::ProviderChain, nil] signature_provider external RBS signature provider
|
|
839
|
+
# @return [Docscribe::Types::MethodSignature, nil]
|
|
840
|
+
def resolve_external_signature(insertion, signature_provider)
|
|
841
|
+
node_name = SourceHelpers.node_name(insertion.node) #: Symbol
|
|
842
|
+
signature_provider&.signature_for(
|
|
557
843
|
container: insertion.container,
|
|
558
844
|
scope: insertion.scope,
|
|
559
|
-
name:
|
|
845
|
+
name: node_name
|
|
560
846
|
)
|
|
561
|
-
|
|
562
|
-
method_override.is_a?(Hash) ? method_override[:return_type] : nil
|
|
563
|
-
|
|
564
|
-
override_param_types =
|
|
565
|
-
method_override.is_a?(Hash) && method_override[:param_types].is_a?(Hash) ? method_override[:param_types] : nil
|
|
566
|
-
|
|
567
|
-
override_tags =
|
|
568
|
-
if method_override.is_a?(Hash)
|
|
569
|
-
Array(method_override[:tags]).filter_map do |t|
|
|
570
|
-
case t
|
|
571
|
-
when Docscribe::Plugin::Tag then t
|
|
572
|
-
when Hash
|
|
573
|
-
Docscribe::Plugin::Tag.new(**t.transform_keys(&:to_sym))
|
|
574
|
-
end
|
|
575
|
-
end
|
|
576
|
-
else
|
|
577
|
-
[]
|
|
578
|
-
end
|
|
579
|
-
|
|
580
|
-
case strategy
|
|
581
|
-
when :aggressive
|
|
582
|
-
if (range = method_comment_block_removal_range(buffer, insertion))
|
|
583
|
-
rewriter.remove(range)
|
|
584
|
-
end
|
|
847
|
+
end
|
|
585
848
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
849
|
+
# Resolve param types
|
|
850
|
+
#
|
|
851
|
+
# @private
|
|
852
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
853
|
+
# @param [Docscribe::Types::MethodSignature, nil] external_sig the resolved signature from the signature provider
|
|
854
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
855
|
+
# @return [Hash<String, String>, nil]
|
|
856
|
+
def resolve_param_types(insertion, external_sig, config)
|
|
857
|
+
if external_sig
|
|
858
|
+
DocBuilder.build_param_types_from_node(
|
|
859
|
+
insertion.node, external_sig: external_sig, config: config
|
|
590
860
|
)
|
|
861
|
+
else
|
|
862
|
+
DocBuilder.build_param_types_from_node(
|
|
863
|
+
insertion.node, external_sig: nil, config: config
|
|
864
|
+
)
|
|
865
|
+
end
|
|
866
|
+
end
|
|
591
867
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
868
|
+
# Apply method insertion aggressive
|
|
869
|
+
#
|
|
870
|
+
# @private
|
|
871
|
+
# @param [Object] options keyword options
|
|
872
|
+
# @return [void]
|
|
873
|
+
def apply_method_insertion_aggressive!(**options)
|
|
874
|
+
rewriter = options[:rewriter]
|
|
875
|
+
buffer = options[:buffer]
|
|
876
|
+
insertion = options[:insertion]
|
|
877
|
+
doc = options[:doc]
|
|
878
|
+
|
|
879
|
+
remove_method_comment_block(rewriter, buffer, insertion)
|
|
880
|
+
return if doc.nil? || doc.empty?
|
|
881
|
+
|
|
882
|
+
rewriter.insert_before(options[:anchor_bol_range], doc)
|
|
883
|
+
add_change(changes: options[:changes], type: :insert_full_doc_block,
|
|
884
|
+
insertion: insertion, file: options[:file], message: 'missing docs')
|
|
885
|
+
end
|
|
595
886
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
887
|
+
# Remove method comment block
|
|
888
|
+
#
|
|
889
|
+
# @private
|
|
890
|
+
# @param [Parser::Source::TreeRewriter] rewriter the TreeRewriter accumulating source transformations
|
|
891
|
+
# @param [Parser::Source::Buffer] buffer the source buffer being rewritten
|
|
892
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
893
|
+
# @return [void]
|
|
894
|
+
def remove_method_comment_block(rewriter, buffer, insertion)
|
|
895
|
+
range = method_comment_block_removal_range(buffer, insertion)
|
|
896
|
+
rewriter.remove(range) if range
|
|
897
|
+
end
|
|
605
898
|
|
|
606
|
-
|
|
899
|
+
# Apply method insertion safe
|
|
900
|
+
#
|
|
901
|
+
# @private
|
|
902
|
+
# @param [Object] options keyword options
|
|
903
|
+
# @return [void]
|
|
904
|
+
def apply_method_insertion_safe!(**options)
|
|
905
|
+
info = method_doc_comment_info(options[:buffer], options[:insertion])
|
|
607
906
|
|
|
608
|
-
|
|
907
|
+
if info
|
|
908
|
+
apply_method_insertion_safe_with_info!(**options, info: info)
|
|
909
|
+
else
|
|
910
|
+
apply_method_insertion_safe_without_info!(**options)
|
|
911
|
+
end
|
|
912
|
+
end
|
|
609
913
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
914
|
+
# Apply method insertion safe with info
|
|
915
|
+
#
|
|
916
|
+
# @private
|
|
917
|
+
# @param [Object] options keyword options
|
|
918
|
+
# @return [void]
|
|
919
|
+
def apply_method_insertion_safe_with_info!(**options)
|
|
920
|
+
i = options[:info]
|
|
921
|
+
dp = filter_doc_params(options)
|
|
922
|
+
mr = DocBuilder.build_missing_merge_result( # steep:ignore
|
|
923
|
+
options[:insertion], existing_lines: i[:doc_lines], strategy: options[:strategy], **dp
|
|
924
|
+
)
|
|
925
|
+
changed, n, ob = compute_doc_replacement(i, mr[:lines], strategy: options[:strategy], **dp)
|
|
926
|
+
commit_safe_doc_outcome(options[:rewriter], options[:buffer], i, n,
|
|
927
|
+
old_block: ob, merge_result: mr, existing_order_changed: changed,
|
|
928
|
+
insertion: options[:insertion], changes: options[:changes], file: options[:file])
|
|
929
|
+
end
|
|
617
930
|
|
|
618
|
-
|
|
619
|
-
|
|
931
|
+
# Commit safe doc outcome
|
|
932
|
+
#
|
|
933
|
+
# @private
|
|
934
|
+
# @param [Parser::Source::TreeRewriter] rewriter the TreeRewriter accumulating source transformations
|
|
935
|
+
# @param [Parser::Source::Buffer] buffer the source buffer being rewritten
|
|
936
|
+
# @param [Hash<Symbol, Object>] info hash containing existing doc comment block data
|
|
937
|
+
# @param [String] new_block the newly constructed replacement doc block string
|
|
938
|
+
# @param [Object] rest additional kwargs (old_block, merge_result,
|
|
939
|
+
# @return [void]
|
|
940
|
+
def commit_safe_doc_outcome(rewriter, buffer, info, new_block, **rest)
|
|
941
|
+
handle_doc_replacement(rewriter, buffer, info, new_block,
|
|
942
|
+
insertion: rest[:insertion], changes: rest[:changes],
|
|
943
|
+
file: rest[:file],
|
|
944
|
+
existing_order_changed: rest[:existing_order_changed])
|
|
945
|
+
log_method_doc_changes!(insertion: rest[:insertion], merge_result: rest[:merge_result],
|
|
946
|
+
new_block: new_block, old_block: rest[:old_block],
|
|
947
|
+
changes: rest[:changes], file: rest[:file])
|
|
948
|
+
end
|
|
620
949
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
950
|
+
# Filter doc params
|
|
951
|
+
#
|
|
952
|
+
# @private
|
|
953
|
+
# @param [Hash<Symbol, Object>] options the full options hash to filter
|
|
954
|
+
# @return [Hash<Symbol, Object>]
|
|
955
|
+
def filter_doc_params(options)
|
|
956
|
+
options.reject { |k, _| %i[rewriter buffer insertion anchor_bol_range info changes file strategy].include?(k) }
|
|
957
|
+
end
|
|
626
958
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
959
|
+
# Handle doc replacement
|
|
960
|
+
#
|
|
961
|
+
# @private
|
|
962
|
+
# @param [Parser::Source::TreeRewriter] rewriter the TreeRewriter accumulating source transformations
|
|
963
|
+
# @param [Parser::Source::Buffer] buffer the source buffer being rewritten
|
|
964
|
+
# @param [Hash<Symbol, Object>] info hash containing existing doc comment block data (start_pos, end_pos, lines)
|
|
965
|
+
# @param [String] new_block the newly constructed replacement doc block string
|
|
966
|
+
# @param [Object] log_opts additional keyword arguments for logging and recording changes
|
|
967
|
+
# @return [void]
|
|
968
|
+
def handle_doc_replacement(rewriter, buffer, info, new_block, **log_opts)
|
|
969
|
+
range = Parser::Source::Range.new(buffer, info[:start_pos], info[:end_pos])
|
|
970
|
+
rewriter.replace(range, new_block)
|
|
630
971
|
|
|
631
|
-
|
|
632
|
-
merge_result = build_missing_method_merge_result(
|
|
633
|
-
insertion,
|
|
634
|
-
existing_lines: info[:doc_lines],
|
|
635
|
-
config: config,
|
|
636
|
-
signature_provider: signature_provider,
|
|
637
|
-
core_rbs_provider: core_rbs_provider,
|
|
638
|
-
param_types: effective_param_types,
|
|
639
|
-
strategy: strategy,
|
|
640
|
-
return_type_override: override_return_type,
|
|
641
|
-
override_tags: override_tags
|
|
642
|
-
)
|
|
643
|
-
|
|
644
|
-
missing_lines = merge_result[:lines]
|
|
645
|
-
reason_specs = merge_result[:reasons] || []
|
|
646
|
-
|
|
647
|
-
sorted_existing_doc_lines = Docscribe::InlineRewriter::DocBlock.merge(
|
|
648
|
-
info[:doc_lines],
|
|
649
|
-
missing_lines: [],
|
|
650
|
-
sort_tags: config.sort_tags?,
|
|
651
|
-
tag_order: config.tag_order
|
|
652
|
-
)
|
|
653
|
-
|
|
654
|
-
merged_doc_lines = Docscribe::InlineRewriter::DocBlock.merge(
|
|
655
|
-
info[:doc_lines],
|
|
656
|
-
missing_lines: missing_lines,
|
|
657
|
-
sort_tags: config.sort_tags?,
|
|
658
|
-
tag_order: config.tag_order
|
|
659
|
-
)
|
|
660
|
-
|
|
661
|
-
existing_order_changed = sorted_existing_doc_lines != info[:doc_lines]
|
|
662
|
-
new_block = (info[:preserved_lines] + merged_doc_lines).join
|
|
663
|
-
old_block = info[:lines].join
|
|
664
|
-
|
|
665
|
-
if new_block != old_block
|
|
666
|
-
range = Parser::Source::Range.new(buffer, info[:start_pos], info[:end_pos])
|
|
667
|
-
rewriter.replace(range, new_block)
|
|
668
|
-
|
|
669
|
-
if existing_order_changed
|
|
670
|
-
add_change(
|
|
671
|
-
changes,
|
|
672
|
-
type: :unsorted_tags,
|
|
673
|
-
insertion: insertion,
|
|
674
|
-
file: file,
|
|
675
|
-
message: 'unsorted tags'
|
|
676
|
-
)
|
|
677
|
-
end
|
|
678
|
-
end
|
|
679
|
-
|
|
680
|
-
type_mismatch_reasons = reason_specs.select { |r| %i[updated_param updated_return].include?(r[:type]) }
|
|
681
|
-
|
|
682
|
-
if new_block != old_block || type_mismatch_reasons.any?
|
|
683
|
-
reason_specs.each do |reason|
|
|
684
|
-
add_change(
|
|
685
|
-
changes,
|
|
686
|
-
type: reason[:type],
|
|
687
|
-
insertion: insertion,
|
|
688
|
-
file: file,
|
|
689
|
-
message: reason[:message],
|
|
690
|
-
extra: reason[:extra] || {}
|
|
691
|
-
)
|
|
692
|
-
end
|
|
693
|
-
end
|
|
694
|
-
|
|
695
|
-
return
|
|
696
|
-
end
|
|
972
|
+
return unless log_opts[:existing_order_changed]
|
|
697
973
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
974
|
+
add_change(changes: log_opts[:changes], type: :unsorted_tags,
|
|
975
|
+
insertion: log_opts[:insertion], file: log_opts[:file],
|
|
976
|
+
message: 'unsorted tags')
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
# Compute doc replacement
|
|
980
|
+
#
|
|
981
|
+
# @private
|
|
982
|
+
# @param [Hash<Symbol, Object>] info existing doc info
|
|
983
|
+
# @param [Array<String>] missing_lines new doc lines to add
|
|
984
|
+
# @param [Object] options keyword options
|
|
985
|
+
# @return [(Boolean, String, String)]
|
|
986
|
+
def compute_doc_replacement(info, missing_lines, **options)
|
|
987
|
+
dc = options[:config]
|
|
988
|
+
sorted = Docscribe::InlineRewriter::DocBlock.merge(
|
|
989
|
+
info[:doc_lines], missing_lines: [], sort_tags: dc.sort_tags?, tag_order: dc.tag_order
|
|
990
|
+
)
|
|
991
|
+
merged = Docscribe::InlineRewriter::DocBlock.merge(
|
|
992
|
+
info[:doc_lines], missing_lines: missing_lines, sort_tags: dc.sort_tags?, tag_order: dc.tag_order
|
|
993
|
+
)
|
|
994
|
+
[sorted != info[:doc_lines], (info[:preserved_lines] + merged).join, info[:lines].join]
|
|
995
|
+
end
|
|
708
996
|
|
|
709
|
-
|
|
997
|
+
# Log method doc changes
|
|
998
|
+
#
|
|
999
|
+
# @private
|
|
1000
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
1001
|
+
# @param [Hash<Symbol, Object>] merge_result merge operation result
|
|
1002
|
+
# @param [Object] rest additional keyword arguments forwarded to add_change
|
|
1003
|
+
# @return [void]
|
|
1004
|
+
def log_method_doc_changes!(insertion:, merge_result:, **rest)
|
|
1005
|
+
reason_specs = merge_result[:reasons] || []
|
|
1006
|
+
type_mismatch_reasons = reason_specs.select { |r| %i[updated_param updated_return].include?(r[:type]) }
|
|
710
1007
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
message: 'missing docs'
|
|
717
|
-
)
|
|
1008
|
+
return unless rest[:new_block] != rest[:old_block] || type_mismatch_reasons.any?
|
|
1009
|
+
|
|
1010
|
+
reason_specs.each do |reason|
|
|
1011
|
+
add_change(changes: rest[:changes], type: reason[:type], insertion: insertion,
|
|
1012
|
+
file: rest[:file], message: reason[:message], extra: reason[:extra] || {})
|
|
718
1013
|
end
|
|
719
1014
|
end
|
|
720
1015
|
|
|
721
|
-
#
|
|
1016
|
+
# Apply method insertion safe without info
|
|
1017
|
+
#
|
|
1018
|
+
# @private
|
|
1019
|
+
# @param [Object] options keyword options
|
|
1020
|
+
# @return [void]
|
|
1021
|
+
def apply_method_insertion_safe_without_info!(**options)
|
|
1022
|
+
rewriter = options[:rewriter]
|
|
1023
|
+
insertion = options[:insertion]
|
|
1024
|
+
anchor_bol_range = options[:anchor_bol_range]
|
|
1025
|
+
doc = DocBuilder.build(insertion, **options.reject do |k, _|
|
|
1026
|
+
%i[rewriter buffer insertion anchor_bol_range changes file strategy].include?(k)
|
|
1027
|
+
end) # steep:ignore
|
|
1028
|
+
return if doc.nil? || doc.empty?
|
|
1029
|
+
|
|
1030
|
+
rewriter.insert_before(anchor_bol_range, doc)
|
|
1031
|
+
add_change(changes: options[:changes], type: :insert_full_doc_block,
|
|
1032
|
+
insertion: insertion, file: options[:file], message: 'missing docs')
|
|
1033
|
+
end
|
|
1034
|
+
|
|
1035
|
+
# Filter options to keep only doc-building params for safe-without-info mode.
|
|
1036
|
+
# @private
|
|
1037
|
+
# @param [Object] options the full options hash to filter
|
|
1038
|
+
# @return [Object]
|
|
1039
|
+
|
|
1040
|
+
# Add change
|
|
722
1041
|
#
|
|
723
1042
|
# @private
|
|
724
|
-
# @param [
|
|
725
|
-
# @param [Symbol] type
|
|
726
|
-
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
727
|
-
# @param [String] file
|
|
728
|
-
# @param [String] message
|
|
729
|
-
# @param [Integer, nil] line
|
|
730
|
-
# @param [Hash] extra
|
|
1043
|
+
# @param [Object] options kwargs for change record (type, file, line, method, message, insertion, changes, extra)
|
|
731
1044
|
# @return [void]
|
|
732
|
-
def add_change(
|
|
1045
|
+
def add_change(**options)
|
|
1046
|
+
changes = options[:changes]
|
|
733
1047
|
changes << {
|
|
734
|
-
type: type,
|
|
735
|
-
file: file,
|
|
736
|
-
line: line || method_line_for(insertion),
|
|
737
|
-
method: method_id_for(insertion),
|
|
738
|
-
message: message
|
|
739
|
-
}.merge(extra)
|
|
1048
|
+
type: options[:type],
|
|
1049
|
+
file: options[:file],
|
|
1050
|
+
line: options[:line] || method_line_for(options[:insertion]),
|
|
1051
|
+
method: method_id_for(options[:insertion]),
|
|
1052
|
+
message: options[:message]
|
|
1053
|
+
}.merge(options[:extra] || {})
|
|
740
1054
|
end
|
|
741
1055
|
|
|
742
|
-
#
|
|
1056
|
+
# Method id for
|
|
743
1057
|
#
|
|
744
1058
|
# @private
|
|
745
|
-
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
1059
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
746
1060
|
# @return [String]
|
|
747
1061
|
def method_id_for(insertion)
|
|
748
1062
|
name = SourceHelpers.node_name(insertion.node)
|
|
749
1063
|
"#{insertion.container}#{insertion.scope == :instance ? '#' : '.'}#{name}"
|
|
750
1064
|
end
|
|
751
1065
|
|
|
752
|
-
# Apply
|
|
1066
|
+
# Apply attr insertion
|
|
753
1067
|
#
|
|
754
1068
|
# @private
|
|
755
|
-
# @param [
|
|
756
|
-
# @param [Parser::Source::Buffer] buffer
|
|
757
|
-
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] insertion
|
|
758
|
-
# @param [Docscribe::Config] config
|
|
759
|
-
# @param [Object, nil] signature_provider
|
|
760
|
-
# @param [Symbol] strategy
|
|
761
|
-
# @param [Hash] merge_inserts
|
|
1069
|
+
# @param [Object] options kwargs (insertion, config, rewriter, buffer, strategy,
|
|
762
1070
|
# @return [void]
|
|
763
|
-
def apply_attr_insertion!(
|
|
1071
|
+
def apply_attr_insertion!(**options)
|
|
1072
|
+
config = options[:config]
|
|
764
1073
|
return unless config.respond_to?(:emit_attributes?) && config.emit_attributes?
|
|
765
|
-
return unless attribute_allowed?(config, insertion)
|
|
1074
|
+
return unless attribute_allowed?(config, options[:insertion])
|
|
766
1075
|
|
|
767
|
-
bol_range = SourceHelpers.line_start_range(buffer, insertion.node)
|
|
1076
|
+
bol_range = SourceHelpers.line_start_range(options[:buffer], options[:insertion].node)
|
|
1077
|
+
params = attr_insertion_params(options[:insertion], config, options[:signature_provider], bol_range)
|
|
1078
|
+
dispatch_attr_strategy(params, options)
|
|
1079
|
+
end
|
|
768
1080
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1081
|
+
# Attr insertion params
|
|
1082
|
+
#
|
|
1083
|
+
# @private
|
|
1084
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] insertion the collected attribute insertion
|
|
1085
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
1086
|
+
# @param [Docscribe::Types::ProviderChain, nil] signature_provider external RBS signature provider
|
|
1087
|
+
# @param [Parser::Source::Range] bol_range the beginning-of-line range for the attribute node
|
|
1088
|
+
# @return [Hash<Symbol, Docscribe::InlineRewriter::Collector::AttrInsertion, Docscribe::Config, Docscribe::Types::ProviderChain, nil, Parser::Source::Range>]
|
|
1089
|
+
def attr_insertion_params(insertion, config, signature_provider, bol_range)
|
|
1090
|
+
{
|
|
1091
|
+
insertion: insertion, config: config,
|
|
1092
|
+
signature_provider: signature_provider, bol_range: bol_range
|
|
1093
|
+
}
|
|
1094
|
+
end
|
|
774
1095
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1096
|
+
# Dispatch attr strategy
|
|
1097
|
+
#
|
|
1098
|
+
# @private
|
|
1099
|
+
# @param [Hash<Symbol, Object>] params precomputed attribute insertion parameters
|
|
1100
|
+
# @param [Hash<Symbol, Object>] options the full keyword options hash
|
|
1101
|
+
# @return [void]
|
|
1102
|
+
def dispatch_attr_strategy(params, options)
|
|
1103
|
+
case options[:strategy]
|
|
1104
|
+
when :aggressive then apply_attr_aggressive!(params, options[:rewriter], options[:buffer])
|
|
1105
|
+
when :safe then apply_attr_safe!(params, options[:merge_inserts], options[:rewriter], options[:buffer])
|
|
1106
|
+
end
|
|
1107
|
+
end
|
|
781
1108
|
|
|
782
|
-
|
|
1109
|
+
# Apply attr aggressive
|
|
1110
|
+
#
|
|
1111
|
+
# @private
|
|
1112
|
+
# @param [Hash<Symbol, Object>] params precomputed attribute insertion parameters
|
|
1113
|
+
# @param [Parser::Source::TreeRewriter] rewriter the TreeRewriter accumulating source transformations
|
|
1114
|
+
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
1115
|
+
# @return [void]
|
|
1116
|
+
def apply_attr_aggressive!(params, rewriter, buffer)
|
|
1117
|
+
if (range = SourceHelpers.comment_block_removal_range(buffer, params[:bol_range].begin_pos))
|
|
1118
|
+
rewriter.remove(range)
|
|
1119
|
+
end
|
|
783
1120
|
|
|
784
|
-
|
|
785
|
-
|
|
1121
|
+
doc = build_attr_doc_for_node(params[:insertion], config: params[:config],
|
|
1122
|
+
signature_provider: params[:signature_provider])
|
|
1123
|
+
return if doc.nil? || doc.empty?
|
|
786
1124
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
insertion,
|
|
790
|
-
existing_lines: info[:lines],
|
|
791
|
-
config: config,
|
|
792
|
-
signature_provider: signature_provider
|
|
793
|
-
)
|
|
1125
|
+
rewriter.insert_before(params[:bol_range], doc)
|
|
1126
|
+
end
|
|
794
1127
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1128
|
+
# Apply attr safe
|
|
1129
|
+
#
|
|
1130
|
+
# @private
|
|
1131
|
+
# @param [Hash<Symbol, Object>] params precomputed attribute insertion parameters
|
|
1132
|
+
# @param [Hash<Integer, Array<(Integer, String)>>] merge_inserts deferred merge inserts
|
|
1133
|
+
# @param [Parser::Source::TreeRewriter] rewriter the TreeRewriter accumulating source transformations
|
|
1134
|
+
# @param [Parser::Source::Buffer] buffer the source buffer being rewritten
|
|
1135
|
+
# @return [void]
|
|
1136
|
+
def apply_attr_safe!(params, merge_inserts, rewriter, buffer)
|
|
1137
|
+
info = SourceHelpers.doc_comment_block_info(buffer, params[:bol_range].begin_pos)
|
|
798
1138
|
|
|
799
|
-
|
|
800
|
-
|
|
1139
|
+
if info
|
|
1140
|
+
merge_attr_additions!(insertion: params[:insertion], info: info, merge_inserts: merge_inserts,
|
|
1141
|
+
config: params[:config], signature_provider: params[:signature_provider])
|
|
1142
|
+
return
|
|
1143
|
+
end
|
|
801
1144
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
signature_provider: signature_provider
|
|
806
|
-
)
|
|
807
|
-
return if doc.nil? || doc.empty?
|
|
1145
|
+
doc = build_attr_doc_for_node(params[:insertion], config: params[:config],
|
|
1146
|
+
signature_provider: params[:signature_provider])
|
|
1147
|
+
return if doc.nil? || doc.empty?
|
|
808
1148
|
|
|
809
|
-
|
|
810
|
-
end
|
|
1149
|
+
rewriter.insert_before(params[:bol_range], doc)
|
|
811
1150
|
end
|
|
812
1151
|
|
|
813
|
-
#
|
|
1152
|
+
# Merge attr additions
|
|
814
1153
|
#
|
|
815
|
-
#
|
|
1154
|
+
# @private
|
|
1155
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] insertion the collected attribute insertion
|
|
1156
|
+
# @param [Hash<Symbol, Object>] info hash containing existing doc comment block data
|
|
1157
|
+
# @param [Hash<Integer, Array<(Integer, String)>>] merge_inserts deferred merge inserts
|
|
1158
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
1159
|
+
# @param [Docscribe::Types::ProviderChain, nil] signature_provider external RBS signature provider
|
|
1160
|
+
# @return [void]
|
|
1161
|
+
def merge_attr_additions!(insertion:, info:, merge_inserts:, config:, signature_provider:)
|
|
1162
|
+
additions = build_attr_merge_additions(ins: insertion, existing_lines: info[:lines],
|
|
1163
|
+
config: config, signature_provider: signature_provider)
|
|
1164
|
+
return unless additions && !additions.empty?
|
|
1165
|
+
|
|
1166
|
+
merge_inserts[info[:end_pos]] << [insertion.node.loc.expression.begin_pos, additions]
|
|
1167
|
+
end
|
|
1168
|
+
|
|
1169
|
+
# Apply merge inserts
|
|
816
1170
|
#
|
|
817
1171
|
# @private
|
|
818
|
-
# @param [Parser::Source::TreeRewriter] rewriter
|
|
819
|
-
# @param [Parser::Source::Buffer] buffer
|
|
820
|
-
# @param [Hash
|
|
1172
|
+
# @param [Parser::Source::TreeRewriter] rewriter the TreeRewriter accumulating source transformations
|
|
1173
|
+
# @param [Parser::Source::Buffer] buffer the source buffer being rewritten
|
|
1174
|
+
# @param [Hash<Integer, Array<(Integer, String)>>] merge_inserts deferred merge inserts
|
|
821
1175
|
# @return [void]
|
|
822
1176
|
def apply_merge_inserts!(rewriter:, buffer:, merge_inserts:)
|
|
823
|
-
sep_re = /^\s*#\s*\r?\n$/
|
|
824
|
-
|
|
825
1177
|
merge_inserts.keys.sort.reverse_each do |end_pos|
|
|
826
|
-
|
|
827
|
-
next if
|
|
828
|
-
|
|
829
|
-
chunks = chunks.sort_by { |(sort_key, _s)| sort_key }
|
|
830
|
-
|
|
831
|
-
out_lines = []
|
|
832
|
-
|
|
833
|
-
chunks.each do |(_k, chunk)|
|
|
834
|
-
next if chunk.nil? || chunk.empty?
|
|
835
|
-
|
|
836
|
-
lines = chunk.lines
|
|
837
|
-
seps = []
|
|
838
|
-
seps << lines.shift while !lines.empty? && lines.first.match?(sep_re)
|
|
839
|
-
|
|
840
|
-
sep = seps.first
|
|
841
|
-
out_lines << sep if sep && (out_lines.empty? || !out_lines.last.match?(sep_re))
|
|
842
|
-
out_lines.concat(lines)
|
|
843
|
-
end
|
|
844
|
-
|
|
845
|
-
text = out_lines.join
|
|
846
|
-
next if text.empty?
|
|
1178
|
+
text = merge_text_for_pos(merge_inserts[end_pos])
|
|
1179
|
+
next if text.nil? || text.empty?
|
|
847
1180
|
|
|
848
1181
|
range = Parser::Source::Range.new(buffer, end_pos, end_pos)
|
|
849
1182
|
rewriter.insert_before(range, text)
|
|
850
1183
|
end
|
|
851
1184
|
end
|
|
852
1185
|
|
|
853
|
-
#
|
|
1186
|
+
# Merge text for pos
|
|
854
1187
|
#
|
|
855
1188
|
# @private
|
|
856
|
-
# @param [
|
|
857
|
-
# @param [Array<String>] existing_lines
|
|
858
|
-
# @param [Docscribe::Config] config
|
|
859
|
-
# @param [Object] signature_provider Param documentation.
|
|
860
|
-
# @raise [StandardError]
|
|
1189
|
+
# @param [Array<(Integer, String)>] chunks merge chunks at position
|
|
861
1190
|
# @return [String, nil]
|
|
862
|
-
def
|
|
863
|
-
|
|
864
|
-
param_tag_style = config.param_tag_style
|
|
865
|
-
existing = existing_attr_names(existing_lines)
|
|
866
|
-
missing = ins.names.reject { |name_sym| existing[name_sym.to_s] }
|
|
867
|
-
return '' if missing.empty?
|
|
1191
|
+
def merge_text_for_pos(chunks)
|
|
1192
|
+
return nil if chunks.empty?
|
|
868
1193
|
|
|
869
|
-
|
|
870
|
-
|
|
1194
|
+
chunks = chunks.sort_by { |(sort_key, _s)| sort_key }
|
|
1195
|
+
out_lines = [] #: Array[String]
|
|
1196
|
+
sep_re = /^\s*#\s*\r?\n$/
|
|
871
1197
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
mode = ins.access.to_s
|
|
875
|
-
attr_type = attribute_type(ins, name_sym, config, signature_provider: signature_provider)
|
|
1198
|
+
chunks.each do |(_k, chunk)|
|
|
1199
|
+
next if chunk.nil? || chunk.empty?
|
|
876
1200
|
|
|
877
|
-
|
|
1201
|
+
merge_chunk_into_out(chunk, out_lines, sep_re)
|
|
1202
|
+
end
|
|
878
1203
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
end
|
|
1204
|
+
text = out_lines.join
|
|
1205
|
+
text.empty? ? nil : text
|
|
1206
|
+
end
|
|
883
1207
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1208
|
+
# Merge chunk into out
|
|
1209
|
+
#
|
|
1210
|
+
# @private
|
|
1211
|
+
# @param [String] chunk the doc text string to merge
|
|
1212
|
+
# @param [Array<String>] out_lines the accumulated output lines array
|
|
1213
|
+
# @param [Regexp] sep_re regex matching separator comment lines (# followed by newline)
|
|
1214
|
+
# @return [void]
|
|
1215
|
+
def merge_chunk_into_out(chunk, out_lines, sep_re)
|
|
1216
|
+
lines = chunk.lines
|
|
1217
|
+
seps = extract_separators(lines, sep_re)
|
|
1218
|
+
sep = seps.first
|
|
1219
|
+
out_lines << sep if sep && (out_lines.empty? || !out_lines.last.match?(sep_re))
|
|
1220
|
+
out_lines.concat(lines)
|
|
1221
|
+
end
|
|
1222
|
+
|
|
1223
|
+
# Extract separators
|
|
1224
|
+
#
|
|
1225
|
+
# @private
|
|
1226
|
+
# @param [Array<String>] lines array of lines from the chunk
|
|
1227
|
+
# @param [Regexp] sep_re regex matching separator comment lines
|
|
1228
|
+
# @return [Array<String>]
|
|
1229
|
+
def extract_separators(lines, sep_re)
|
|
1230
|
+
seps = [] #: Array[String]
|
|
1231
|
+
seps << lines.shift while !lines.empty? && lines.first.match?(sep_re)
|
|
1232
|
+
seps
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1235
|
+
# Build attr merge additions
|
|
1236
|
+
#
|
|
1237
|
+
# @private
|
|
1238
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
1239
|
+
# @param [Array<String>] existing_lines array of existing doc comment lines
|
|
1240
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
1241
|
+
# @param [Docscribe::Types::ProviderChain, nil] signature_provider external RBS signature provider
|
|
1242
|
+
# @raise [StandardError]
|
|
1243
|
+
# @return [String, nil] if StandardError
|
|
1244
|
+
# @return [nil] if StandardError
|
|
1245
|
+
def build_attr_merge_additions(ins:, existing_lines:, config:, signature_provider:)
|
|
1246
|
+
missing = missing_attr_names(ins, existing_lines)
|
|
1247
|
+
return '' if missing.empty?
|
|
890
1248
|
|
|
1249
|
+
indent = SourceHelpers.line_indent(ins.node)
|
|
1250
|
+
lines = [] #: Array[String]
|
|
1251
|
+
lines << "#{indent}#" if existing_lines.any? && existing_lines.last.strip != '#'
|
|
1252
|
+
lines.concat(build_attr_doc_lines(ins, indent: indent, config: config,
|
|
1253
|
+
signature_provider: signature_provider, names: missing))
|
|
891
1254
|
lines.map { |l| "#{l}\n" }.join
|
|
892
1255
|
rescue StandardError
|
|
893
1256
|
nil
|
|
894
1257
|
end
|
|
895
1258
|
|
|
896
|
-
#
|
|
1259
|
+
# Missing attr names
|
|
1260
|
+
#
|
|
1261
|
+
# @private
|
|
1262
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
1263
|
+
# @param [Array<String>] existing_lines array of existing doc comment lines
|
|
1264
|
+
# @return [Array<Symbol>]
|
|
1265
|
+
def missing_attr_names(ins, existing_lines)
|
|
1266
|
+
existing = existing_attr_names(existing_lines)
|
|
1267
|
+
ins.names.reject { |name_sym| existing[name_sym.to_s] }
|
|
1268
|
+
end
|
|
1269
|
+
|
|
1270
|
+
# Existing attr names
|
|
897
1271
|
#
|
|
898
1272
|
# @private
|
|
899
|
-
# @param [Array<String>] lines
|
|
900
|
-
# @return [Hash
|
|
1273
|
+
# @param [Array<String>] lines array of existing doc comment lines
|
|
1274
|
+
# @return [Hash<String, nil, Boolean>]
|
|
901
1275
|
def existing_attr_names(lines)
|
|
902
|
-
names = {}
|
|
1276
|
+
names = {} #: Hash[String, bool]
|
|
903
1277
|
|
|
904
1278
|
Array(lines).each do |line|
|
|
905
1279
|
if (m = line.match(/^\s*#\s*@!attribute\b(?:\s+\[[^\]]+\])?\s+(\S+)/))
|
|
906
|
-
names[m[1]] = true
|
|
1280
|
+
names[m[1].to_s] = true
|
|
907
1281
|
end
|
|
908
1282
|
end
|
|
909
1283
|
|
|
910
1284
|
names
|
|
911
1285
|
end
|
|
912
1286
|
|
|
913
|
-
#
|
|
1287
|
+
# Attribute allowed
|
|
914
1288
|
#
|
|
915
1289
|
# @private
|
|
916
|
-
# @param [Docscribe::Config] config
|
|
917
|
-
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins
|
|
1290
|
+
# @param [Docscribe::Config] config the active configuration
|
|
1291
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
918
1292
|
# @return [Boolean]
|
|
919
1293
|
def attribute_allowed?(config, ins)
|
|
920
1294
|
ins.names.any? do |name_sym|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
ok ||= config.process_method?(
|
|
925
|
-
container: ins.container,
|
|
926
|
-
scope: ins.scope,
|
|
927
|
-
visibility: ins.visibility,
|
|
928
|
-
name: name_sym
|
|
929
|
-
)
|
|
930
|
-
end
|
|
1295
|
+
allowed_for_access?(config, ins, name_sym)
|
|
1296
|
+
end
|
|
1297
|
+
end
|
|
931
1298
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1299
|
+
# Allowed for access
|
|
1300
|
+
#
|
|
1301
|
+
# @private
|
|
1302
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
1303
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
1304
|
+
# @param [Symbol] name_sym the attribute name as a Symbol
|
|
1305
|
+
# @return [Boolean]
|
|
1306
|
+
def allowed_for_access?(config, ins, name_sym)
|
|
1307
|
+
ok = false
|
|
1308
|
+
|
|
1309
|
+
if %i[r rw].include?(ins.access)
|
|
1310
|
+
ok ||= config.process_method?(container: ins.container, scope: ins.scope,
|
|
1311
|
+
visibility: ins.visibility, name: name_sym)
|
|
1312
|
+
end
|
|
940
1313
|
|
|
941
|
-
|
|
1314
|
+
if %i[w rw].include?(ins.access)
|
|
1315
|
+
ok ||= config.process_method?(container: ins.container, scope: ins.scope,
|
|
1316
|
+
visibility: ins.visibility, name: :"#{name_sym}=")
|
|
942
1317
|
end
|
|
1318
|
+
|
|
1319
|
+
ok
|
|
943
1320
|
end
|
|
944
1321
|
|
|
945
|
-
# Build
|
|
1322
|
+
# Build attr doc for node
|
|
946
1323
|
#
|
|
947
1324
|
# @private
|
|
948
|
-
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins
|
|
949
|
-
# @param [Docscribe::Config] config
|
|
950
|
-
# @param [
|
|
1325
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
1326
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
1327
|
+
# @param [Docscribe::Types::ProviderChain, nil] signature_provider external RBS signature provider
|
|
951
1328
|
# @raise [StandardError]
|
|
952
|
-
# @return [String, nil]
|
|
1329
|
+
# @return [String, nil] if StandardError
|
|
1330
|
+
# @return [nil] if StandardError
|
|
953
1331
|
def build_attr_doc_for_node(ins, config:, signature_provider:)
|
|
954
1332
|
indent = SourceHelpers.line_indent(ins.node)
|
|
955
|
-
|
|
956
|
-
lines
|
|
1333
|
+
lines = build_attr_doc_lines(ins, indent: indent, config: config, signature_provider: signature_provider)
|
|
1334
|
+
lines.map { |l| "#{l}\n" }.join
|
|
1335
|
+
rescue StandardError
|
|
1336
|
+
nil
|
|
1337
|
+
end
|
|
957
1338
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1339
|
+
# Build attr doc lines
|
|
1340
|
+
#
|
|
1341
|
+
# @private
|
|
1342
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
1343
|
+
# @param [String] indent whitespace indentation prefix derived from the attribute node
|
|
1344
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
1345
|
+
# @param [Docscribe::Types::ProviderChain, nil] signature_provider external RBS signature provider
|
|
1346
|
+
# @param [Array<Symbol>, nil?] names optional subset of attribute names to document (defaults to all names)
|
|
1347
|
+
# @return [Array<String>]
|
|
1348
|
+
def build_attr_doc_lines(ins, indent:, config:, signature_provider:, names: nil)
|
|
1349
|
+
names ||= ins.names
|
|
1350
|
+
lines = [] #: Array[untyped]
|
|
1351
|
+
|
|
1352
|
+
names.each_with_index do |name_sym, idx|
|
|
1353
|
+
lines.concat(build_single_attr_lines(ins, name_sym, indent: indent,
|
|
1354
|
+
config: config, signature_provider: signature_provider,
|
|
1355
|
+
idx: idx, total: names.length))
|
|
1356
|
+
end
|
|
962
1357
|
|
|
963
|
-
|
|
1358
|
+
lines
|
|
1359
|
+
end
|
|
964
1360
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1361
|
+
# Build single attr lines
|
|
1362
|
+
#
|
|
1363
|
+
# @private
|
|
1364
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
1365
|
+
# @param [Symbol] name_sym the attribute name as a Symbol
|
|
1366
|
+
# @param [String] indent whitespace indentation prefix
|
|
1367
|
+
# @param [Object] opts additional keyword arguments forwarded from build_attr_doc_lines
|
|
1368
|
+
# @return [Array<String>]
|
|
1369
|
+
def build_single_attr_lines(ins, name_sym, indent:, **opts)
|
|
1370
|
+
cfg = opts[:config]
|
|
1371
|
+
attr_type = attribute_type(ins, name_sym, cfg, signature_provider: opts[:signature_provider])
|
|
1372
|
+
lines = ["#{indent}# @!attribute [#{ins.access}] #{name_sym}"]
|
|
1373
|
+
lines.concat(attr_visibility_lines(indent, cfg, ins))
|
|
1374
|
+
append_attr_return_tag(lines, indent, attr_type, ins.access)
|
|
1375
|
+
append_attr_param_tag(lines, indent, attr_type, ins.access, cfg)
|
|
1376
|
+
lines << "#{indent}#" if opts[:idx] < opts[:total] - 1
|
|
1377
|
+
lines
|
|
1378
|
+
end
|
|
969
1379
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1380
|
+
# Append attr return tag
|
|
1381
|
+
#
|
|
1382
|
+
# @private
|
|
1383
|
+
# @param [Array<String>] lines the doc lines array being built
|
|
1384
|
+
# @param [String] indent whitespace indentation prefix
|
|
1385
|
+
# @param [String] attr_type the resolved type string for the attribute
|
|
1386
|
+
# @param [Symbol] access the access level (:r, :w, or :rw)
|
|
1387
|
+
# @return [void]
|
|
1388
|
+
def append_attr_return_tag(lines, indent, attr_type, access)
|
|
1389
|
+
lines << "#{indent}# @return [#{attr_type}]" if %i[r rw].include?(access)
|
|
1390
|
+
end
|
|
974
1391
|
|
|
975
|
-
|
|
976
|
-
|
|
1392
|
+
# Append attr param tag
|
|
1393
|
+
#
|
|
1394
|
+
# @private
|
|
1395
|
+
# @param [Array<String>] lines the doc lines array being built
|
|
1396
|
+
# @param [String] indent whitespace indentation prefix
|
|
1397
|
+
# @param [String] attr_type the resolved type string for the attribute
|
|
1398
|
+
# @param [Symbol] access the access level (:r, :w, or :rw)
|
|
1399
|
+
# @param [Docscribe::Config] cfg the active Docscribe::Config
|
|
1400
|
+
# @return [void]
|
|
1401
|
+
def append_attr_param_tag(lines, indent, attr_type, access, cfg)
|
|
1402
|
+
return unless %i[w rw].include?(access)
|
|
977
1403
|
|
|
978
|
-
lines
|
|
979
|
-
|
|
980
|
-
|
|
1404
|
+
lines << format_attribute_param_tag(indent, 'value', attr_type, style: cfg.param_tag_style)
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
# Attr visibility lines
|
|
1408
|
+
#
|
|
1409
|
+
# @private
|
|
1410
|
+
# @param [String] indent whitespace indentation prefix
|
|
1411
|
+
# @param [Docscribe::Config] config the active Docscribe::Config
|
|
1412
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
1413
|
+
# @return [Array<String>]
|
|
1414
|
+
def attr_visibility_lines(indent, config, ins)
|
|
1415
|
+
return [] unless config.emit_visibility_tags?
|
|
1416
|
+
|
|
1417
|
+
lines = [] #: Array[String]
|
|
1418
|
+
lines << "#{indent}# @private" if ins.visibility == :private
|
|
1419
|
+
lines << "#{indent}# @protected" if ins.visibility == :protected
|
|
1420
|
+
lines
|
|
981
1421
|
end
|
|
982
1422
|
|
|
983
|
-
# Format
|
|
1423
|
+
# Format attribute param tag
|
|
984
1424
|
#
|
|
985
1425
|
# @private
|
|
986
1426
|
# @param [String] indent leading whitespace
|
|
987
|
-
# @param [
|
|
1427
|
+
# @param [String] name attribute name
|
|
988
1428
|
# @param [String] type attribute type
|
|
989
1429
|
# @param [String, Symbol] style param tag style (`"name_type"` or `"type_name"`)
|
|
990
1430
|
# @return [String] formatted doc line
|
|
@@ -999,17 +1439,16 @@ module Docscribe
|
|
|
999
1439
|
end
|
|
1000
1440
|
end
|
|
1001
1441
|
|
|
1002
|
-
#
|
|
1003
|
-
#
|
|
1004
|
-
# Prefers the RBS reader signature when available; otherwise falls back to the config fallback type.
|
|
1442
|
+
# Attribute type
|
|
1005
1443
|
#
|
|
1006
1444
|
# @private
|
|
1007
|
-
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins
|
|
1008
|
-
# @param [Symbol] name_sym
|
|
1009
|
-
# @param [Docscribe::Config] config
|
|
1010
|
-
# @param [
|
|
1445
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins the attribute insertion object
|
|
1446
|
+
# @param [Symbol] name_sym the attribute name as a Symbol
|
|
1447
|
+
# @param [Docscribe::Config] config the active configuration
|
|
1448
|
+
# @param [Docscribe::Types::ProviderChain, nil] signature_provider RBS signature provider
|
|
1011
1449
|
# @raise [StandardError]
|
|
1012
|
-
# @return [String]
|
|
1450
|
+
# @return [String] if StandardError
|
|
1451
|
+
# @return [Object] if StandardError
|
|
1013
1452
|
def attribute_type(ins, name_sym, config, signature_provider:)
|
|
1014
1453
|
ty = config.fallback_type
|
|
1015
1454
|
return ty unless signature_provider
|
|
@@ -1020,21 +1459,20 @@ module Docscribe
|
|
|
1020
1459
|
config.fallback_type
|
|
1021
1460
|
end
|
|
1022
1461
|
|
|
1023
|
-
# Build
|
|
1024
|
-
#
|
|
1025
|
-
# Checks config methods in order: `signature_provider_for`, `signature_provider`, `rbs_provider`.
|
|
1462
|
+
# Build signature provider
|
|
1026
1463
|
#
|
|
1027
1464
|
# @private
|
|
1028
1465
|
# @param [Docscribe::Config] config the active configuration
|
|
1029
1466
|
# @param [String] code the source code being processed
|
|
1030
1467
|
# @param [String] file the file name
|
|
1031
1468
|
# @raise [StandardError]
|
|
1032
|
-
# @return [Object, nil]
|
|
1469
|
+
# @return [Object, nil] if StandardError
|
|
1470
|
+
# @return [Object?] if StandardError
|
|
1033
1471
|
def build_signature_provider(config, code, file)
|
|
1034
1472
|
if config.respond_to?(:signature_provider_for)
|
|
1035
1473
|
config.signature_provider_for(source: code, file: file)
|
|
1036
1474
|
elsif config.respond_to?(:signature_provider)
|
|
1037
|
-
config.signature_provider
|
|
1475
|
+
config.signature_provider # steep:ignore
|
|
1038
1476
|
elsif config.respond_to?(:rbs_provider)
|
|
1039
1477
|
config.rbs_provider
|
|
1040
1478
|
end
|
|
@@ -1042,64 +1480,12 @@ module Docscribe
|
|
|
1042
1480
|
config.respond_to?(:rbs_provider) ? config.rbs_provider : nil
|
|
1043
1481
|
end
|
|
1044
1482
|
|
|
1045
|
-
#
|
|
1046
|
-
#
|
|
1047
|
-
# @private
|
|
1048
|
-
# @param [Collector::Insertion] insertion the collected method insertion
|
|
1049
|
-
# @param [Docscribe::Config] config the active configuration
|
|
1050
|
-
# @param [Object, nil] signature_provider external signature provider
|
|
1051
|
-
# @param [Object, nil] core_rbs_provider RBS core type provider
|
|
1052
|
-
# @param [Hash, nil] param_types parameter name -> type map
|
|
1053
|
-
# @param [Object] return_type_override Param documentation.
|
|
1054
|
-
# @param [Object] override_tags Param documentation.
|
|
1055
|
-
# @return [String, nil] generated doc block or nil
|
|
1056
|
-
def build_method_doc(insertion, config:, signature_provider:, core_rbs_provider:, param_types:, return_type_override:,
|
|
1057
|
-
override_tags:)
|
|
1058
|
-
DocBuilder.build(
|
|
1059
|
-
insertion,
|
|
1060
|
-
config: config,
|
|
1061
|
-
signature_provider: signature_provider,
|
|
1062
|
-
core_rbs_provider: core_rbs_provider,
|
|
1063
|
-
param_types: param_types,
|
|
1064
|
-
return_type_override: return_type_override,
|
|
1065
|
-
override_tags: override_tags
|
|
1066
|
-
)
|
|
1067
|
-
end
|
|
1068
|
-
|
|
1069
|
-
# Delegate to DocBuilder.build_missing_merge_result for generating missing doc lines only.
|
|
1070
|
-
#
|
|
1071
|
-
# @private
|
|
1072
|
-
# @param [Collector::Insertion] insertion the collected method insertion
|
|
1073
|
-
# @param [Array<String>] existing_lines existing doc-like lines
|
|
1074
|
-
# @param [Docscribe::Config] config the active configuration
|
|
1075
|
-
# @param [Object, nil] signature_provider external signature provider
|
|
1076
|
-
# @param [Object, nil] core_rbs_provider RBS core type provider
|
|
1077
|
-
# @param [Hash, nil] param_types parameter name -> type map
|
|
1078
|
-
# @param [Object] strategy Param documentation.
|
|
1079
|
-
# @param [Object] return_type_override Param documentation.
|
|
1080
|
-
# @param [nil] override_tags Param documentation.
|
|
1081
|
-
# @return [Hash] result with `:lines` and `:reasons` keys
|
|
1082
|
-
def build_missing_method_merge_result(insertion, existing_lines:, config:, signature_provider:,
|
|
1083
|
-
core_rbs_provider:, param_types:, strategy:, return_type_override:, override_tags: nil)
|
|
1084
|
-
DocBuilder.build_missing_merge_result(
|
|
1085
|
-
insertion,
|
|
1086
|
-
existing_lines: existing_lines,
|
|
1087
|
-
config: config,
|
|
1088
|
-
signature_provider: signature_provider,
|
|
1089
|
-
core_rbs_provider: core_rbs_provider,
|
|
1090
|
-
param_types: param_types,
|
|
1091
|
-
strategy: strategy,
|
|
1092
|
-
return_type_override: return_type_override,
|
|
1093
|
-
override_tags: override_tags
|
|
1094
|
-
)
|
|
1095
|
-
end
|
|
1096
|
-
|
|
1097
|
-
# Get doc comment block info (preceding comments) for a method insertion.
|
|
1483
|
+
# Method doc comment info
|
|
1098
1484
|
#
|
|
1099
1485
|
# @private
|
|
1100
1486
|
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
1101
|
-
# @param [Collector::Insertion] insertion the collected method insertion
|
|
1102
|
-
# @return [Hash, nil] doc comment block info or nil
|
|
1487
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
1488
|
+
# @return [Hash<Symbol, Object>, nil] doc comment block info or nil
|
|
1103
1489
|
def method_doc_comment_info(buffer, insertion)
|
|
1104
1490
|
anchor_bol_range, def_bol_range = method_bol_ranges(buffer, insertion)
|
|
1105
1491
|
|
|
@@ -1107,11 +1493,11 @@ module Docscribe
|
|
|
1107
1493
|
SourceHelpers.doc_comment_block_info(buffer, def_bol_range.begin_pos)
|
|
1108
1494
|
end
|
|
1109
1495
|
|
|
1110
|
-
#
|
|
1496
|
+
# Method comment block removal range
|
|
1111
1497
|
#
|
|
1112
1498
|
# @private
|
|
1113
1499
|
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
1114
|
-
# @param [Collector::Insertion] insertion the collected method insertion
|
|
1500
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
1115
1501
|
# @return [Parser::Source::Range, nil]
|
|
1116
1502
|
def method_comment_block_removal_range(buffer, insertion)
|
|
1117
1503
|
anchor_bol_range, def_bol_range = method_bol_ranges(buffer, insertion)
|
|
@@ -1120,12 +1506,12 @@ module Docscribe
|
|
|
1120
1506
|
SourceHelpers.comment_block_removal_range(buffer, def_bol_range.begin_pos)
|
|
1121
1507
|
end
|
|
1122
1508
|
|
|
1123
|
-
#
|
|
1509
|
+
# Method bol ranges
|
|
1124
1510
|
#
|
|
1125
1511
|
# @private
|
|
1126
1512
|
# @param [Parser::Source::Buffer] buffer the source buffer
|
|
1127
|
-
# @param [Collector::Insertion] insertion the collected method insertion
|
|
1128
|
-
# @return [
|
|
1513
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
1514
|
+
# @return [(Parser::Source::Range, Parser::Source::Range)]
|
|
1129
1515
|
def method_bol_ranges(buffer, insertion)
|
|
1130
1516
|
anchor_node = anchor_node_for(insertion)
|
|
1131
1517
|
[
|
|
@@ -1134,22 +1520,23 @@ module Docscribe
|
|
|
1134
1520
|
]
|
|
1135
1521
|
end
|
|
1136
1522
|
|
|
1137
|
-
#
|
|
1523
|
+
# Method line for
|
|
1138
1524
|
#
|
|
1139
1525
|
# @private
|
|
1140
|
-
# @param [Collector::Insertion] insertion the collected method insertion
|
|
1526
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
1141
1527
|
# @raise [StandardError]
|
|
1142
|
-
# @return [Integer]
|
|
1528
|
+
# @return [Integer] if StandardError
|
|
1529
|
+
# @return [Object] if StandardError
|
|
1143
1530
|
def method_line_for(insertion)
|
|
1144
1531
|
anchor_node_for(insertion).loc.expression.line
|
|
1145
1532
|
rescue StandardError
|
|
1146
1533
|
insertion.node.loc.expression.line
|
|
1147
1534
|
end
|
|
1148
1535
|
|
|
1149
|
-
#
|
|
1536
|
+
# Anchor node for
|
|
1150
1537
|
#
|
|
1151
1538
|
# @private
|
|
1152
|
-
# @param [Collector::Insertion] insertion the collected method insertion
|
|
1539
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion the collected method insertion
|
|
1153
1540
|
# @return [Parser::AST::Node]
|
|
1154
1541
|
def anchor_node_for(insertion)
|
|
1155
1542
|
if insertion.respond_to?(:anchor_node) && insertion.anchor_node
|
|
@@ -1158,6 +1545,36 @@ module Docscribe
|
|
|
1158
1545
|
insertion.node
|
|
1159
1546
|
end
|
|
1160
1547
|
end
|
|
1548
|
+
|
|
1549
|
+
# Extract method override
|
|
1550
|
+
#
|
|
1551
|
+
# @private
|
|
1552
|
+
# @param [Hash<Symbol, Object>, nil] method_override the raw override data
|
|
1553
|
+
# @return [Hash<Symbol, Object>] normalized override hash
|
|
1554
|
+
def extract_method_override!(method_override)
|
|
1555
|
+
return {} unless method_override.is_a?(Hash)
|
|
1556
|
+
|
|
1557
|
+
{
|
|
1558
|
+
return_type: method_override[:return_type],
|
|
1559
|
+
param_types: method_override[:param_types].is_a?(Hash) ? method_override[:param_types] : {},
|
|
1560
|
+
tags: normalize_override_tags(method_override[:tags])
|
|
1561
|
+
}
|
|
1562
|
+
end
|
|
1563
|
+
|
|
1564
|
+
# Normalize override tags
|
|
1565
|
+
#
|
|
1566
|
+
# @private
|
|
1567
|
+
# @param [Array<Object>] tags raw tag values
|
|
1568
|
+
# @return [Array<Docscribe::Plugin::Tag>]
|
|
1569
|
+
def normalize_override_tags(tags)
|
|
1570
|
+
Array(tags).filter_map do |tag|
|
|
1571
|
+
case tag
|
|
1572
|
+
when Docscribe::Plugin::Tag then tag
|
|
1573
|
+
when Hash
|
|
1574
|
+
Docscribe::Plugin::Tag.new(**tag.transform_keys(&:to_sym))
|
|
1575
|
+
end
|
|
1576
|
+
end
|
|
1577
|
+
end
|
|
1161
1578
|
end
|
|
1162
1579
|
end
|
|
1163
1580
|
end
|