docscribe 1.1.0 → 1.2.1
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 +662 -187
- data/exe/docscribe +2 -126
- data/lib/docscribe/cli/config_builder.rb +62 -0
- data/lib/docscribe/cli/init.rb +58 -0
- data/lib/docscribe/cli/options.rb +204 -0
- data/lib/docscribe/cli/run.rb +415 -0
- data/lib/docscribe/cli.rb +31 -0
- data/lib/docscribe/config/defaults.rb +71 -0
- data/lib/docscribe/config/emit.rb +142 -0
- data/lib/docscribe/config/filtering.rb +160 -0
- data/lib/docscribe/config/loader.rb +59 -0
- data/lib/docscribe/config/rbs.rb +51 -0
- data/lib/docscribe/config/sorbet.rb +87 -0
- data/lib/docscribe/config/sorting.rb +23 -0
- data/lib/docscribe/config/template.rb +184 -0
- data/lib/docscribe/config/utils.rb +102 -0
- data/lib/docscribe/config.rb +20 -230
- data/lib/docscribe/infer/ast_walk.rb +28 -0
- data/lib/docscribe/infer/constants.rb +11 -0
- data/lib/docscribe/infer/literals.rb +55 -0
- data/lib/docscribe/infer/names.rb +43 -0
- data/lib/docscribe/infer/params.rb +62 -0
- data/lib/docscribe/infer/raises.rb +68 -0
- data/lib/docscribe/infer/returns.rb +171 -0
- data/lib/docscribe/infer.rb +104 -258
- data/lib/docscribe/inline_rewriter/collector.rb +845 -0
- data/lib/docscribe/inline_rewriter/doc_block.rb +383 -0
- data/lib/docscribe/inline_rewriter/doc_builder.rb +607 -0
- data/lib/docscribe/inline_rewriter/source_helpers.rb +228 -0
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +244 -0
- data/lib/docscribe/inline_rewriter.rb +599 -428
- data/lib/docscribe/parsing.rb +55 -44
- data/lib/docscribe/types/provider_chain.rb +37 -0
- data/lib/docscribe/types/rbs/provider.rb +213 -0
- data/lib/docscribe/types/rbs/type_formatter.rb +132 -0
- data/lib/docscribe/types/signature.rb +65 -0
- data/lib/docscribe/types/sorbet/base_provider.rb +217 -0
- data/lib/docscribe/types/sorbet/rbi_provider.rb +35 -0
- data/lib/docscribe/types/sorbet/source_provider.rb +25 -0
- data/lib/docscribe/version.rb +1 -1
- metadata +37 -3
|
@@ -1,540 +1,711 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'racc/parser'
|
|
4
3
|
require 'ast'
|
|
5
4
|
require 'parser/deprecation'
|
|
6
5
|
require 'parser/source/buffer'
|
|
7
6
|
require 'parser/source/range'
|
|
8
7
|
require 'parser/source/tree_rewriter'
|
|
9
|
-
require 'parser/ast/processor'
|
|
10
8
|
|
|
11
9
|
require 'docscribe/config'
|
|
12
|
-
require 'docscribe/infer'
|
|
13
10
|
require 'docscribe/parsing'
|
|
14
11
|
|
|
12
|
+
require 'docscribe/inline_rewriter/source_helpers'
|
|
13
|
+
require 'docscribe/inline_rewriter/doc_builder'
|
|
14
|
+
require 'docscribe/inline_rewriter/collector'
|
|
15
|
+
require 'docscribe/inline_rewriter/doc_block'
|
|
16
|
+
|
|
15
17
|
module Docscribe
|
|
18
|
+
# Raised when source cannot be parsed before rewriting.
|
|
19
|
+
class ParseError < StandardError; end
|
|
20
|
+
|
|
21
|
+
# Rewrite Ruby source to insert or update inline YARD-style documentation.
|
|
22
|
+
#
|
|
23
|
+
# Supported strategies:
|
|
24
|
+
# - `:safe`
|
|
25
|
+
# - insert missing docs
|
|
26
|
+
# - merge into existing doc-like blocks
|
|
27
|
+
# - normalize configured sortable tags
|
|
28
|
+
# - preserve existing prose and directives where possible
|
|
29
|
+
# - `:aggressive`
|
|
30
|
+
# - replace existing doc blocks with freshly generated docs
|
|
31
|
+
#
|
|
32
|
+
# Compatibility note:
|
|
33
|
+
# - `merge: true` maps to `strategy: :safe`
|
|
34
|
+
# - `rewrite: true` maps to `strategy: :aggressive`
|
|
16
35
|
module InlineRewriter
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
collector.insertions
|
|
39
|
-
.sort_by { |ins| ins.node.loc.expression.begin_pos }
|
|
40
|
-
.reverse_each do |ins|
|
|
41
|
-
bol_range = line_start_range(buffer, ins.node)
|
|
42
|
-
|
|
43
|
-
if rewrite
|
|
44
|
-
# If there is a comment block immediately above, remove it (and its trailing blank lines)
|
|
45
|
-
if (range = comment_block_removal_range(buffer, bol_range.begin_pos))
|
|
46
|
-
rewriter.remove(range)
|
|
47
|
-
end
|
|
48
|
-
elsif already_has_doc_immediately_above?(buffer, bol_range.begin_pos)
|
|
49
|
-
# Skip if a doc already exists immediately above
|
|
50
|
-
next
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
doc = build_doc_for_node(buffer, ins, config)
|
|
54
|
-
next unless doc && !doc.empty?
|
|
55
|
-
|
|
56
|
-
rewriter.insert_before(bol_range, doc)
|
|
36
|
+
class << self
|
|
37
|
+
# Rewrite source and return only the rewritten output string.
|
|
38
|
+
#
|
|
39
|
+
# This is the main convenience entry point for library usage.
|
|
40
|
+
#
|
|
41
|
+
# @param [String] code Ruby source
|
|
42
|
+
# @param [Symbol, nil] strategy :safe or :aggressive
|
|
43
|
+
# @param [Boolean, nil] rewrite compatibility alias for aggressive strategy
|
|
44
|
+
# @param [Boolean, nil] merge compatibility alias for safe strategy
|
|
45
|
+
# @param [Docscribe::Config, nil] config config object (defaults to loaded config)
|
|
46
|
+
# @param [String] file source name used for parser locations/debugging
|
|
47
|
+
# @return [String]
|
|
48
|
+
def insert_comments(code, strategy: nil, rewrite: nil, merge: nil, config: nil, file: '(inline)')
|
|
49
|
+
strategy = normalize_strategy(strategy: strategy, rewrite: rewrite, merge: merge)
|
|
50
|
+
|
|
51
|
+
rewrite_with_report(
|
|
52
|
+
code,
|
|
53
|
+
strategy: strategy,
|
|
54
|
+
config: config,
|
|
55
|
+
file: file
|
|
56
|
+
)[:output]
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
# Rewrite source and return both output and structured change information.
|
|
60
|
+
#
|
|
61
|
+
# The result hash includes:
|
|
62
|
+
# - `:output` => rewritten source
|
|
63
|
+
# - `:changes` => structured change records used by CLI explanation output
|
|
64
|
+
#
|
|
65
|
+
# @param [String] code Ruby source
|
|
66
|
+
# @param [Symbol, nil] strategy :safe or :aggressive
|
|
67
|
+
# @param [Boolean, nil] rewrite compatibility alias for aggressive strategy
|
|
68
|
+
# @param [Boolean, nil] merge compatibility alias for safe strategy
|
|
69
|
+
# @param [Docscribe::Config, nil] config config object (defaults to loaded config)
|
|
70
|
+
# @param [String] file source name used for parser locations/debugging
|
|
71
|
+
# @raise [Docscribe::ParseError]
|
|
72
|
+
# @return [Hash]
|
|
73
|
+
def rewrite_with_report(code, strategy: nil, rewrite: nil, merge: nil, config: nil, file: '(inline)')
|
|
74
|
+
strategy = normalize_strategy(strategy: strategy, rewrite: rewrite, merge: merge)
|
|
75
|
+
validate_strategy!(strategy)
|
|
76
|
+
|
|
77
|
+
buffer = Parser::Source::Buffer.new(file.to_s, source: code)
|
|
78
|
+
ast = Docscribe::Parsing.parse_buffer(buffer)
|
|
79
|
+
raise Docscribe::ParseError, "Failed to parse #{file}" unless ast
|
|
80
|
+
|
|
81
|
+
config ||= Docscribe::Config.load
|
|
82
|
+
signature_provider = build_signature_provider(config, code, file.to_s)
|
|
83
|
+
|
|
84
|
+
collector = Docscribe::InlineRewriter::Collector.new(buffer)
|
|
85
|
+
collector.process(ast)
|
|
86
|
+
|
|
87
|
+
method_insertions = collector.insertions
|
|
88
|
+
attr_insertions = collector.respond_to?(:attr_insertions) ? collector.attr_insertions : []
|
|
89
|
+
|
|
90
|
+
all = method_insertions.map { |i| [:method, i] } + attr_insertions.map { |i| [:attr, i] }
|
|
91
|
+
|
|
92
|
+
rewriter = Parser::Source::TreeRewriter.new(buffer)
|
|
93
|
+
merge_inserts = Hash.new { |h, k| h[k] = [] }
|
|
94
|
+
changes = []
|
|
95
|
+
|
|
96
|
+
all.sort_by { |(_kind, ins)| ins.node.loc.expression.begin_pos }
|
|
97
|
+
.reverse_each do |kind, ins|
|
|
98
|
+
case kind
|
|
99
|
+
when :method
|
|
100
|
+
apply_method_insertion!(
|
|
101
|
+
rewriter: rewriter,
|
|
102
|
+
buffer: buffer,
|
|
103
|
+
insertion: ins,
|
|
104
|
+
config: config,
|
|
105
|
+
signature_provider: signature_provider,
|
|
106
|
+
strategy: strategy,
|
|
107
|
+
changes: changes,
|
|
108
|
+
file: file.to_s
|
|
109
|
+
)
|
|
110
|
+
when :attr
|
|
111
|
+
apply_attr_insertion!(
|
|
112
|
+
rewriter: rewriter,
|
|
113
|
+
buffer: buffer,
|
|
114
|
+
insertion: ins,
|
|
115
|
+
config: config,
|
|
116
|
+
signature_provider: signature_provider,
|
|
117
|
+
strategy: strategy,
|
|
118
|
+
merge_inserts: merge_inserts
|
|
119
|
+
)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
61
122
|
|
|
62
|
-
|
|
63
|
-
#
|
|
64
|
-
# Method documentation.
|
|
65
|
-
#
|
|
66
|
-
# @param [Object] buffer Param documentation.
|
|
67
|
-
# @param [Object] node Param documentation.
|
|
68
|
-
# @return [Range]
|
|
69
|
-
def self.line_start_range(buffer, node)
|
|
70
|
-
start_pos = node.loc.expression.begin_pos
|
|
71
|
-
src = buffer.source
|
|
72
|
-
bol = src.rindex("\n", start_pos - 1) || -1
|
|
73
|
-
Parser::Source::Range.new(buffer, bol + 1, bol + 1)
|
|
74
|
-
end
|
|
123
|
+
apply_merge_inserts!(rewriter: rewriter, buffer: buffer, merge_inserts: merge_inserts)
|
|
75
124
|
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
# Method documentation.
|
|
79
|
-
#
|
|
80
|
-
# @param [Object] node Param documentation.
|
|
81
|
-
# @return [Object]
|
|
82
|
-
def self.node_name(node)
|
|
83
|
-
case node.type
|
|
84
|
-
when :def
|
|
85
|
-
node.children[0]
|
|
86
|
-
when :defs
|
|
87
|
-
node.children[1] # method name symbol
|
|
125
|
+
{ output: rewriter.process, changes: changes }
|
|
88
126
|
end
|
|
89
|
-
end
|
|
90
127
|
|
|
91
|
-
|
|
92
|
-
#
|
|
93
|
-
# Method documentation.
|
|
94
|
-
#
|
|
95
|
-
# @param [Object] buffer Param documentation.
|
|
96
|
-
# @param [Object] def_bol_pos Param documentation.
|
|
97
|
-
# @return [Range]
|
|
98
|
-
def self.comment_block_removal_range(buffer, def_bol_pos)
|
|
99
|
-
src = buffer.source
|
|
100
|
-
lines = src.lines
|
|
101
|
-
# Find def line index
|
|
102
|
-
def_line_idx = src[0...def_bol_pos].count("\n")
|
|
103
|
-
i = def_line_idx - 1
|
|
104
|
-
|
|
105
|
-
# Walk up and skip blank lines directly above def
|
|
106
|
-
i -= 1 while i >= 0 && lines[i].strip.empty?
|
|
107
|
-
# Now if the nearest non-blank line isn't a comment, nothing to remove
|
|
108
|
-
return nil unless i >= 0 && lines[i] =~ /^\s*#/
|
|
109
|
-
|
|
110
|
-
# Find the start of the contiguous comment block
|
|
111
|
-
start_idx = i
|
|
112
|
-
start_idx -= 1 while start_idx >= 0 && lines[start_idx] =~ /^\s*#/
|
|
113
|
-
start_idx += 1
|
|
114
|
-
|
|
115
|
-
# End position is at def_bol_pos; start position is BOL of start_idx
|
|
116
|
-
# Compute absolute buffer positions
|
|
117
|
-
# Position of BOL for start_idx:
|
|
118
|
-
start_pos = 0
|
|
119
|
-
if start_idx.positive?
|
|
120
|
-
# Sum lengths of all preceding lines
|
|
121
|
-
start_pos = lines[0...start_idx].join.length
|
|
122
|
-
end
|
|
128
|
+
private
|
|
123
129
|
|
|
124
|
-
|
|
125
|
-
|
|
130
|
+
# Normalize strategy inputs, including compatibility booleans.
|
|
131
|
+
#
|
|
132
|
+
# Precedence:
|
|
133
|
+
# - explicit `strategy`
|
|
134
|
+
# - `rewrite: true` => `:aggressive`
|
|
135
|
+
# - `merge: true` => `:safe`
|
|
136
|
+
# - default => `:safe`
|
|
137
|
+
#
|
|
138
|
+
# @private
|
|
139
|
+
# @param [Symbol, nil] strategy
|
|
140
|
+
# @param [Boolean, nil] rewrite
|
|
141
|
+
# @param [Boolean, nil] merge
|
|
142
|
+
# @return [Symbol]
|
|
143
|
+
def normalize_strategy(strategy:, rewrite:, merge:)
|
|
144
|
+
return strategy if strategy
|
|
145
|
+
return :aggressive if rewrite
|
|
146
|
+
return :safe if merge
|
|
147
|
+
|
|
148
|
+
:safe
|
|
149
|
+
end
|
|
126
150
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
src = buffer.source
|
|
136
|
-
lines = src.lines
|
|
137
|
-
current_line_index = src[0...insert_pos].count("\n")
|
|
138
|
-
i = current_line_index - 1
|
|
139
|
-
i -= 1 while i >= 0 && lines[i].strip.empty?
|
|
140
|
-
return false if i.negative?
|
|
141
|
-
|
|
142
|
-
!!(lines[i] =~ /^\s*#/)
|
|
143
|
-
end
|
|
151
|
+
# Validate a normalized rewrite strategy.
|
|
152
|
+
#
|
|
153
|
+
# @private
|
|
154
|
+
# @param [Symbol] strategy
|
|
155
|
+
# @raise [ArgumentError]
|
|
156
|
+
# @return [void]
|
|
157
|
+
def validate_strategy!(strategy)
|
|
158
|
+
return if %i[safe aggressive].include?(strategy)
|
|
144
159
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# Method documentation.
|
|
148
|
-
#
|
|
149
|
-
# @param [Object] _buffer Param documentation.
|
|
150
|
-
# @param [Object] insertion Param documentation.
|
|
151
|
-
# @param [Object] config Param documentation.
|
|
152
|
-
# @raise [StandardError]
|
|
153
|
-
# @return [Object]
|
|
154
|
-
# @return [nil] if StandardError
|
|
155
|
-
def self.build_doc_for_node(_buffer, insertion, config)
|
|
156
|
-
node = insertion.node
|
|
157
|
-
indent = ' ' * node.loc.expression.column
|
|
158
|
-
|
|
159
|
-
name =
|
|
160
|
-
case node.type
|
|
161
|
-
when :def then node.children[0]
|
|
162
|
-
when :defs then node.children[1]
|
|
163
|
-
end
|
|
160
|
+
raise ArgumentError, "Unknown strategy: #{strategy.inspect}"
|
|
161
|
+
end
|
|
164
162
|
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
# Apply one method insertion according to the selected strategy.
|
|
164
|
+
#
|
|
165
|
+
# Safe strategy:
|
|
166
|
+
# - merge into existing doc-like blocks when present
|
|
167
|
+
# - otherwise insert a full doc block non-destructively
|
|
168
|
+
#
|
|
169
|
+
# Aggressive strategy:
|
|
170
|
+
# - remove the existing doc block (if any)
|
|
171
|
+
# - insert a fresh regenerated block
|
|
172
|
+
#
|
|
173
|
+
# @private
|
|
174
|
+
# @param [Parser::Source::TreeRewriter] rewriter
|
|
175
|
+
# @param [Parser::Source::Buffer] buffer
|
|
176
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
177
|
+
# @param [Docscribe::Config] config
|
|
178
|
+
# @param [Object, nil] signature_provider
|
|
179
|
+
# @param [Symbol] strategy
|
|
180
|
+
# @param [Array<Hash>] changes structured change records
|
|
181
|
+
# @param [String] file
|
|
182
|
+
# @return [void]
|
|
183
|
+
def apply_method_insertion!(rewriter:, buffer:, insertion:, config:, signature_provider:, strategy:, changes:,
|
|
184
|
+
file:)
|
|
185
|
+
name = SourceHelpers.node_name(insertion.node)
|
|
186
|
+
|
|
187
|
+
return unless config.process_method?(
|
|
188
|
+
container: insertion.container,
|
|
189
|
+
scope: insertion.scope,
|
|
190
|
+
visibility: insertion.visibility,
|
|
191
|
+
name: name
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
anchor_bol_range, = method_bol_ranges(buffer, insertion)
|
|
195
|
+
|
|
196
|
+
case strategy
|
|
197
|
+
when :aggressive
|
|
198
|
+
if (range = method_comment_block_removal_range(buffer, insertion))
|
|
199
|
+
rewriter.remove(range)
|
|
200
|
+
end
|
|
167
201
|
|
|
168
|
-
|
|
169
|
-
|
|
202
|
+
doc = build_method_doc(insertion, config: config, signature_provider: signature_provider)
|
|
203
|
+
return if doc.nil? || doc.empty?
|
|
204
|
+
|
|
205
|
+
rewriter.insert_before(anchor_bol_range, doc)
|
|
206
|
+
|
|
207
|
+
add_change(
|
|
208
|
+
changes,
|
|
209
|
+
type: :insert_full_doc_block,
|
|
210
|
+
insertion: insertion,
|
|
211
|
+
file: file,
|
|
212
|
+
message: 'missing docs'
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
when :safe
|
|
216
|
+
info = method_doc_comment_info(buffer, insertion)
|
|
217
|
+
|
|
218
|
+
if info
|
|
219
|
+
merge_result = build_missing_method_merge_result(
|
|
220
|
+
insertion,
|
|
221
|
+
existing_lines: info[:doc_lines],
|
|
222
|
+
config: config,
|
|
223
|
+
signature_provider: signature_provider
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
missing_lines = merge_result[:lines]
|
|
227
|
+
reason_specs = merge_result[:reasons] || []
|
|
228
|
+
|
|
229
|
+
sorted_existing_doc_lines = Docscribe::InlineRewriter::DocBlock.merge(
|
|
230
|
+
info[:doc_lines],
|
|
231
|
+
missing_lines: [],
|
|
232
|
+
sort_tags: config.sort_tags?,
|
|
233
|
+
tag_order: config.tag_order
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
merged_doc_lines = Docscribe::InlineRewriter::DocBlock.merge(
|
|
237
|
+
info[:doc_lines],
|
|
238
|
+
missing_lines: missing_lines,
|
|
239
|
+
sort_tags: config.sort_tags?,
|
|
240
|
+
tag_order: config.tag_order
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
existing_order_changed = sorted_existing_doc_lines != info[:doc_lines]
|
|
244
|
+
new_block = (info[:preserved_lines] + merged_doc_lines).join
|
|
245
|
+
old_block = info[:lines].join
|
|
246
|
+
|
|
247
|
+
if new_block != old_block
|
|
248
|
+
range = Parser::Source::Range.new(buffer, info[:start_pos], info[:end_pos])
|
|
249
|
+
rewriter.replace(range, new_block)
|
|
250
|
+
|
|
251
|
+
reason_specs.each do |reason|
|
|
252
|
+
add_change(
|
|
253
|
+
changes,
|
|
254
|
+
type: reason[:type],
|
|
255
|
+
insertion: insertion,
|
|
256
|
+
file: file,
|
|
257
|
+
message: reason[:message],
|
|
258
|
+
extra: reason[:extra] || {}
|
|
259
|
+
)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
if existing_order_changed
|
|
263
|
+
add_change(
|
|
264
|
+
changes,
|
|
265
|
+
type: :unsorted_tags,
|
|
266
|
+
insertion: insertion,
|
|
267
|
+
file: file,
|
|
268
|
+
message: 'unsorted tags'
|
|
269
|
+
)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
170
272
|
|
|
171
|
-
|
|
172
|
-
|
|
273
|
+
return
|
|
274
|
+
end
|
|
173
275
|
|
|
174
|
-
|
|
175
|
-
|
|
276
|
+
doc = build_method_doc(insertion, config: config, signature_provider: signature_provider)
|
|
277
|
+
return if doc.nil? || doc.empty?
|
|
176
278
|
|
|
177
|
-
|
|
178
|
-
spec = Docscribe::Infer.returns_spec_from_node(node)
|
|
179
|
-
normal_type = spec[:normal]
|
|
180
|
-
rescue_specs = spec[:rescues]
|
|
279
|
+
rewriter.insert_before(anchor_bol_range, doc)
|
|
181
280
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
281
|
+
add_change(
|
|
282
|
+
changes,
|
|
283
|
+
type: :insert_full_doc_block,
|
|
284
|
+
insertion: insertion,
|
|
285
|
+
file: file,
|
|
286
|
+
message: 'missing docs'
|
|
287
|
+
)
|
|
288
|
+
end
|
|
186
289
|
end
|
|
187
290
|
|
|
188
|
-
#
|
|
189
|
-
|
|
190
|
-
|
|
291
|
+
# Append a structured change record.
|
|
292
|
+
#
|
|
293
|
+
# @private
|
|
294
|
+
# @param [Array<Hash>] changes
|
|
295
|
+
# @param [Symbol] type
|
|
296
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
297
|
+
# @param [String] file
|
|
298
|
+
# @param [String] message
|
|
299
|
+
# @param [Integer, nil] line
|
|
300
|
+
# @param [Hash] extra
|
|
301
|
+
# @return [void]
|
|
302
|
+
def add_change(changes, type:, insertion:, file:, message:, line: nil, extra: {})
|
|
303
|
+
changes << {
|
|
304
|
+
type: type,
|
|
305
|
+
file: file,
|
|
306
|
+
line: line || method_line_for(insertion),
|
|
307
|
+
method: method_id_for(insertion),
|
|
308
|
+
message: message
|
|
309
|
+
}.merge(extra)
|
|
310
|
+
end
|
|
191
311
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
312
|
+
# Build a printable method identifier from a collected insertion.
|
|
313
|
+
#
|
|
314
|
+
# @private
|
|
315
|
+
# @param [Docscribe::InlineRewriter::Collector::Insertion] insertion
|
|
316
|
+
# @return [String]
|
|
317
|
+
def method_id_for(insertion)
|
|
318
|
+
name = SourceHelpers.node_name(insertion.node)
|
|
319
|
+
"#{insertion.container}#{insertion.scope == :instance ? '#' : '.'}#{name}"
|
|
197
320
|
end
|
|
198
321
|
|
|
199
|
-
|
|
322
|
+
# Apply one attribute insertion according to the selected strategy.
|
|
323
|
+
#
|
|
324
|
+
# @private
|
|
325
|
+
# @param [Parser::Source::TreeRewriter] rewriter
|
|
326
|
+
# @param [Parser::Source::Buffer] buffer
|
|
327
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] insertion
|
|
328
|
+
# @param [Docscribe::Config] config
|
|
329
|
+
# @param [Object, nil] signature_provider
|
|
330
|
+
# @param [Symbol] strategy
|
|
331
|
+
# @param [Hash] merge_inserts
|
|
332
|
+
# @return [void]
|
|
333
|
+
def apply_attr_insertion!(rewriter:, buffer:, insertion:, config:, signature_provider:, strategy:, merge_inserts:)
|
|
334
|
+
return unless config.respond_to?(:emit_attributes?) && config.emit_attributes?
|
|
335
|
+
return unless attribute_allowed?(config, insertion)
|
|
336
|
+
|
|
337
|
+
bol_range = SourceHelpers.line_start_range(buffer, insertion.node)
|
|
338
|
+
|
|
339
|
+
case strategy
|
|
340
|
+
when :aggressive
|
|
341
|
+
if (range = SourceHelpers.comment_block_removal_range(buffer, bol_range.begin_pos))
|
|
342
|
+
rewriter.remove(range)
|
|
343
|
+
end
|
|
200
344
|
|
|
201
|
-
|
|
345
|
+
doc = build_attr_doc_for_node(
|
|
346
|
+
insertion,
|
|
347
|
+
config: config,
|
|
348
|
+
signature_provider: signature_provider
|
|
349
|
+
)
|
|
350
|
+
return if doc.nil? || doc.empty?
|
|
202
351
|
|
|
203
|
-
|
|
352
|
+
rewriter.insert_before(bol_range, doc)
|
|
204
353
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
ex_display = exceptions.join(', ')
|
|
208
|
-
lines << "#{indent}# @return [#{rtype}] if #{ex_display}"
|
|
209
|
-
end
|
|
210
|
-
end
|
|
354
|
+
when :safe
|
|
355
|
+
info = SourceHelpers.doc_comment_block_info(buffer, bol_range.begin_pos)
|
|
211
356
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
357
|
+
if info
|
|
358
|
+
additions = build_attr_merge_additions(
|
|
359
|
+
insertion,
|
|
360
|
+
existing_lines: info[:lines],
|
|
361
|
+
config: config,
|
|
362
|
+
signature_provider: signature_provider
|
|
363
|
+
)
|
|
216
364
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
#
|
|
221
|
-
# @param [Object] node Param documentation.
|
|
222
|
-
# @param [Object] indent Param documentation.
|
|
223
|
-
# @return [Object?]
|
|
224
|
-
def self.build_params_block(node, indent)
|
|
225
|
-
args =
|
|
226
|
-
case node.type
|
|
227
|
-
when :def then node.children[1]
|
|
228
|
-
when :defs then node.children[2] # FIX: args is children[2], not [3]
|
|
229
|
-
end
|
|
230
|
-
return nil unless args
|
|
231
|
-
|
|
232
|
-
params = []
|
|
233
|
-
(args.children || []).each do |a|
|
|
234
|
-
case a.type
|
|
235
|
-
when :arg
|
|
236
|
-
name = a.children.first.to_s
|
|
237
|
-
ty = Infer.infer_param_type(name, nil)
|
|
238
|
-
params << "#{indent}# @param [#{ty}] #{name} Param documentation."
|
|
239
|
-
when :optarg
|
|
240
|
-
name, default = *a
|
|
241
|
-
ty = Infer.infer_param_type(name.to_s, default&.loc&.expression&.source)
|
|
242
|
-
params << "#{indent}# @param [#{ty}] #{name} Param documentation."
|
|
243
|
-
when :kwarg
|
|
244
|
-
name = "#{a.children.first}:"
|
|
245
|
-
ty = Infer.infer_param_type(name, nil)
|
|
246
|
-
pname = name.sub(/:$/, '')
|
|
247
|
-
params << "#{indent}# @param [#{ty}] #{pname} Param documentation."
|
|
248
|
-
when :kwoptarg
|
|
249
|
-
name, default = *a
|
|
250
|
-
name = "#{name}:"
|
|
251
|
-
ty = Infer.infer_param_type(name, default&.loc&.expression&.source)
|
|
252
|
-
pname = name.sub(/:$/, '')
|
|
253
|
-
params << "#{indent}# @param [#{ty}] #{pname} Param documentation."
|
|
254
|
-
when :restarg
|
|
255
|
-
name = "*#{a.children.first}"
|
|
256
|
-
ty = Infer.infer_param_type(name, nil)
|
|
257
|
-
pname = a.children.first.to_s
|
|
258
|
-
params << "#{indent}# @param [#{ty}] #{pname} Param documentation."
|
|
259
|
-
when :kwrestarg
|
|
260
|
-
name = "**#{a.children.first || 'kwargs'}"
|
|
261
|
-
ty = Infer.infer_param_type(name, nil)
|
|
262
|
-
pname = (a.children.first || 'kwargs').to_s
|
|
263
|
-
params << "#{indent}# @param [#{ty}] #{pname} Param documentation."
|
|
264
|
-
when :blockarg
|
|
265
|
-
name = "&#{a.children.first}"
|
|
266
|
-
ty = Infer.infer_param_type(name, nil)
|
|
267
|
-
pname = a.children.first.to_s
|
|
268
|
-
params << "#{indent}# @param [#{ty}] #{pname} Param documentation."
|
|
269
|
-
when :forward_arg
|
|
270
|
-
# Ruby 3 '...' forwarding; skip
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
|
-
params.empty? ? nil : params
|
|
274
|
-
end
|
|
365
|
+
if additions && !additions.empty?
|
|
366
|
+
merge_inserts[info[:end_pos]] << [insertion.node.loc.expression.begin_pos, additions]
|
|
367
|
+
end
|
|
275
368
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
attr_reader :explicit_instance, :explicit_class
|
|
369
|
+
return
|
|
370
|
+
end
|
|
279
371
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
@explicit_class = {} # { name_sym => :private|:protected|:public }
|
|
290
|
-
@inside_sclass = false
|
|
372
|
+
doc = build_attr_doc_for_node(
|
|
373
|
+
insertion,
|
|
374
|
+
config: config,
|
|
375
|
+
signature_provider: signature_provider
|
|
376
|
+
)
|
|
377
|
+
return if doc.nil? || doc.empty?
|
|
378
|
+
|
|
379
|
+
rewriter.insert_before(bol_range, doc)
|
|
380
|
+
end
|
|
291
381
|
end
|
|
292
382
|
|
|
293
|
-
#
|
|
383
|
+
# Apply aggregated merge inserts at shared end positions.
|
|
294
384
|
#
|
|
295
|
-
#
|
|
385
|
+
# Used primarily for attribute merge behavior where multiple additions may target the same block end.
|
|
296
386
|
#
|
|
297
|
-
# @
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
c.explicit_class.merge!(explicit_class)
|
|
305
|
-
c
|
|
306
|
-
end
|
|
307
|
-
end
|
|
387
|
+
# @private
|
|
388
|
+
# @param [Parser::Source::TreeRewriter] rewriter
|
|
389
|
+
# @param [Parser::Source::Buffer] buffer
|
|
390
|
+
# @param [Hash{Integer=>Array<(Integer,String)>}] merge_inserts
|
|
391
|
+
# @return [void]
|
|
392
|
+
def apply_merge_inserts!(rewriter:, buffer:, merge_inserts:)
|
|
393
|
+
sep_re = /^\s*#\s*\r?\n$/
|
|
308
394
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
395
|
+
merge_inserts.keys.sort.reverse_each do |end_pos|
|
|
396
|
+
chunks = merge_inserts[end_pos]
|
|
397
|
+
next if chunks.empty?
|
|
312
398
|
|
|
313
|
-
|
|
399
|
+
chunks = chunks.sort_by { |(sort_key, _s)| sort_key }
|
|
314
400
|
|
|
315
|
-
|
|
316
|
-
#
|
|
317
|
-
# Method documentation.
|
|
318
|
-
#
|
|
319
|
-
# @param [Object] buffer Param documentation.
|
|
320
|
-
# @return [Object]
|
|
321
|
-
def initialize(buffer)
|
|
322
|
-
super()
|
|
323
|
-
@buffer = buffer
|
|
324
|
-
@insertions = []
|
|
325
|
-
@name_stack = [] # e.g., ['Demo']
|
|
326
|
-
end
|
|
401
|
+
out_lines = []
|
|
327
402
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
403
|
+
chunks.each do |(_k, chunk)|
|
|
404
|
+
next if chunk.nil? || chunk.empty?
|
|
405
|
+
|
|
406
|
+
lines = chunk.lines
|
|
407
|
+
seps = []
|
|
408
|
+
seps << lines.shift while !lines.empty? && lines.first.match?(sep_re)
|
|
409
|
+
|
|
410
|
+
sep = seps.first
|
|
411
|
+
out_lines << sep if sep && (out_lines.empty? || !out_lines.last.match?(sep_re))
|
|
412
|
+
out_lines.concat(lines)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
text = out_lines.join
|
|
416
|
+
next if text.empty?
|
|
417
|
+
|
|
418
|
+
range = Parser::Source::Range.new(buffer, end_pos, end_pos)
|
|
419
|
+
rewriter.insert_before(range, text)
|
|
420
|
+
end
|
|
341
421
|
end
|
|
342
422
|
|
|
343
|
-
#
|
|
423
|
+
# Build plain-text merge additions for an attribute doc block.
|
|
344
424
|
#
|
|
345
|
-
#
|
|
346
|
-
#
|
|
347
|
-
# @param [
|
|
348
|
-
# @
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
425
|
+
# @private
|
|
426
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins
|
|
427
|
+
# @param [Array<String>] existing_lines
|
|
428
|
+
# @param [Docscribe::Config] config
|
|
429
|
+
# @param [Object] signature_provider Param documentation.
|
|
430
|
+
# @raise [StandardError]
|
|
431
|
+
# @return [String, nil]
|
|
432
|
+
def build_attr_merge_additions(ins, existing_lines:, config:, signature_provider:)
|
|
433
|
+
indent = SourceHelpers.line_indent(ins.node)
|
|
434
|
+
param_tag_style = config.param_tag_style
|
|
435
|
+
existing = existing_attr_names(existing_lines)
|
|
436
|
+
missing = ins.names.reject { |name_sym| existing[name_sym.to_s] }
|
|
437
|
+
return '' if missing.empty?
|
|
438
|
+
|
|
439
|
+
lines = []
|
|
440
|
+
lines << "#{indent}#" if existing_lines.any? && existing_lines.last.strip != '#'
|
|
441
|
+
|
|
442
|
+
missing.each_with_index do |name_sym, idx|
|
|
443
|
+
attr_name = name_sym.to_s
|
|
444
|
+
mode = ins.access.to_s
|
|
445
|
+
attr_type = attribute_type(ins, name_sym, config, signature_provider: signature_provider)
|
|
446
|
+
|
|
447
|
+
lines << "#{indent}# @!attribute [#{mode}] #{attr_name}"
|
|
448
|
+
|
|
449
|
+
if config.emit_visibility_tags?
|
|
450
|
+
lines << "#{indent}# @private" if ins.visibility == :private
|
|
451
|
+
lines << "#{indent}# @protected" if ins.visibility == :protected
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
lines << "#{indent}# @return [#{attr_type}]" if %i[r rw].include?(ins.access)
|
|
455
|
+
if %i[w rw].include?(ins.access)
|
|
456
|
+
lines << format_attribute_param_tag(indent, 'value', attr_type, style: param_tag_style)
|
|
457
|
+
end
|
|
458
|
+
lines << "#{indent}#" if idx < missing.length - 1
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
lines.map { |l| "#{l}\n" }.join
|
|
462
|
+
rescue StandardError
|
|
463
|
+
nil
|
|
356
464
|
end
|
|
357
465
|
|
|
358
|
-
#
|
|
466
|
+
# Extract already documented attribute names from existing `@!attribute` lines.
|
|
359
467
|
#
|
|
360
|
-
#
|
|
361
|
-
#
|
|
362
|
-
# @
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
468
|
+
# @private
|
|
469
|
+
# @param [Array<String>] lines
|
|
470
|
+
# @return [Hash{String=>Boolean}]
|
|
471
|
+
def existing_attr_names(lines)
|
|
472
|
+
names = {}
|
|
473
|
+
|
|
474
|
+
Array(lines).each do |line|
|
|
475
|
+
if (m = line.match(/^\s*#\s*@!attribute\b(?:\s+\[[^\]]+\])?\s+(\S+)/))
|
|
476
|
+
names[m[1]] = true
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
names
|
|
367
481
|
end
|
|
368
482
|
|
|
369
|
-
#
|
|
483
|
+
# Decide whether an attribute macro should be emitted according to method filters.
|
|
370
484
|
#
|
|
371
|
-
#
|
|
372
|
-
#
|
|
373
|
-
# @param [
|
|
374
|
-
# @return [
|
|
375
|
-
def
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
485
|
+
# @private
|
|
486
|
+
# @param [Docscribe::Config] config
|
|
487
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins
|
|
488
|
+
# @return [Boolean]
|
|
489
|
+
def attribute_allowed?(config, ins)
|
|
490
|
+
ins.names.any? do |name_sym|
|
|
491
|
+
ok = false
|
|
492
|
+
|
|
493
|
+
if %i[r rw].include?(ins.access)
|
|
494
|
+
ok ||= config.process_method?(
|
|
495
|
+
container: ins.container,
|
|
496
|
+
scope: ins.scope,
|
|
497
|
+
visibility: ins.visibility,
|
|
498
|
+
name: name_sym
|
|
499
|
+
)
|
|
500
|
+
end
|
|
379
501
|
|
|
380
|
-
|
|
502
|
+
if %i[w rw].include?(ins.access)
|
|
503
|
+
ok ||= config.process_method?(
|
|
504
|
+
container: ins.container,
|
|
505
|
+
scope: ins.scope,
|
|
506
|
+
visibility: ins.visibility,
|
|
507
|
+
name: :"#{name_sym}="
|
|
508
|
+
)
|
|
509
|
+
end
|
|
381
510
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
511
|
+
ok
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
# Build a full `@!attribute` documentation block for one attribute insertion.
|
|
385
516
|
#
|
|
386
517
|
# @private
|
|
387
|
-
# @param [
|
|
388
|
-
# @param [
|
|
389
|
-
# @
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
518
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins
|
|
519
|
+
# @param [Docscribe::Config] config
|
|
520
|
+
# @param [Object] signature_provider Param documentation.
|
|
521
|
+
# @raise [StandardError]
|
|
522
|
+
# @return [String, nil]
|
|
523
|
+
def build_attr_doc_for_node(ins, config:, signature_provider:)
|
|
524
|
+
indent = SourceHelpers.line_indent(ins.node)
|
|
525
|
+
param_tag_style = config.param_tag_style
|
|
526
|
+
lines = []
|
|
527
|
+
|
|
528
|
+
ins.names.each_with_index do |name_sym, idx|
|
|
529
|
+
attr_name = name_sym.to_s
|
|
530
|
+
mode = ins.access.to_s
|
|
531
|
+
attr_type = attribute_type(ins, name_sym, config, signature_provider: signature_provider)
|
|
532
|
+
|
|
533
|
+
lines << "#{indent}# @!attribute [#{mode}] #{attr_name}"
|
|
534
|
+
|
|
535
|
+
if config.emit_visibility_tags?
|
|
536
|
+
lines << "#{indent}# @private" if ins.visibility == :private
|
|
537
|
+
lines << "#{indent}# @protected" if ins.visibility == :protected
|
|
402
538
|
end
|
|
403
|
-
@insertions << Insertion.new(node, scope, vis, current_container)
|
|
404
539
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
540
|
+
lines << "#{indent}# @return [#{attr_type}]" if %i[r rw].include?(ins.access)
|
|
541
|
+
if %i[w rw].include?(ins.access)
|
|
542
|
+
lines << format_attribute_param_tag(indent, 'value', attr_type, style: param_tag_style)
|
|
543
|
+
end
|
|
409
544
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
inner_ctx = ctx.dup
|
|
413
|
-
inner_ctx.inside_sclass = self_node?(recv)
|
|
414
|
-
inner_ctx.default_class_vis = :public
|
|
415
|
-
process_body(body, inner_ctx)
|
|
545
|
+
lines << "#{indent}#" if idx < ins.names.length - 1
|
|
546
|
+
end
|
|
416
547
|
|
|
417
|
-
|
|
418
|
-
|
|
548
|
+
lines.map { |l| "#{l}\n" }.join
|
|
549
|
+
rescue StandardError
|
|
550
|
+
nil
|
|
551
|
+
end
|
|
419
552
|
|
|
553
|
+
# Method documentation.
|
|
554
|
+
#
|
|
555
|
+
# @private
|
|
556
|
+
# @param [Object] indent Param documentation.
|
|
557
|
+
# @param [Object] name Param documentation.
|
|
558
|
+
# @param [Object] type Param documentation.
|
|
559
|
+
# @param [Object] style Param documentation.
|
|
560
|
+
# @return [String]
|
|
561
|
+
def format_attribute_param_tag(indent, name, type, style:)
|
|
562
|
+
type = type.to_s
|
|
563
|
+
|
|
564
|
+
case style.to_s
|
|
565
|
+
when 'name_type'
|
|
566
|
+
"#{indent}# @param #{name} [#{type}]"
|
|
420
567
|
else
|
|
421
|
-
|
|
568
|
+
"#{indent}# @param [#{type}] #{name}"
|
|
422
569
|
end
|
|
423
570
|
end
|
|
424
571
|
|
|
425
|
-
#
|
|
572
|
+
# Determine the attribute type for one attr name.
|
|
573
|
+
#
|
|
574
|
+
# Prefers the RBS reader signature when available; otherwise falls back to the config fallback type.
|
|
426
575
|
#
|
|
576
|
+
# @private
|
|
577
|
+
# @param [Docscribe::InlineRewriter::Collector::AttrInsertion] ins
|
|
578
|
+
# @param [Symbol] name_sym
|
|
579
|
+
# @param [Docscribe::Config] config
|
|
580
|
+
# @param [Object] signature_provider Param documentation.
|
|
581
|
+
# @raise [StandardError]
|
|
582
|
+
# @return [String]
|
|
583
|
+
def attribute_type(ins, name_sym, config, signature_provider:)
|
|
584
|
+
ty = config.fallback_type
|
|
585
|
+
return ty unless signature_provider
|
|
586
|
+
|
|
587
|
+
reader_sig = signature_provider.signature_for(container: ins.container, scope: ins.scope, name: name_sym)
|
|
588
|
+
reader_sig&.return_type || ty
|
|
589
|
+
rescue StandardError
|
|
590
|
+
config.fallback_type
|
|
591
|
+
end
|
|
592
|
+
|
|
427
593
|
# Method documentation.
|
|
428
594
|
#
|
|
429
595
|
# @private
|
|
430
|
-
# @param [Object]
|
|
431
|
-
# @param [Object]
|
|
596
|
+
# @param [Object] config Param documentation.
|
|
597
|
+
# @param [Object] code Param documentation.
|
|
598
|
+
# @param [Object] file Param documentation.
|
|
599
|
+
# @raise [StandardError]
|
|
432
600
|
# @return [Object]
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
else
|
|
442
|
-
ctx.default_instance_vis = meth
|
|
443
|
-
end
|
|
444
|
-
else
|
|
445
|
-
# explicit list: affects current def-target
|
|
446
|
-
args.each do |arg|
|
|
447
|
-
sym = extract_name_sym(arg)
|
|
448
|
-
next unless sym
|
|
449
|
-
|
|
450
|
-
if ctx.inside_sclass
|
|
451
|
-
ctx.explicit_class[sym] = meth
|
|
452
|
-
else
|
|
453
|
-
ctx.explicit_instance[sym] = meth
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
target = ctx.inside_sclass ? 'class' : 'instance'
|
|
457
|
-
if args.empty?
|
|
458
|
-
puts "[vis] bare #{meth} -> default_#{target}_vis=#{meth}"
|
|
459
|
-
else
|
|
460
|
-
puts "[vis] explicit #{meth} -> #{target} names=#{args.map { |a| extract_name_sym(a) }.inspect}"
|
|
461
|
-
end
|
|
462
|
-
end
|
|
601
|
+
# @return [Object?] if StandardError
|
|
602
|
+
def build_signature_provider(config, code, file)
|
|
603
|
+
if config.respond_to?(:signature_provider_for)
|
|
604
|
+
config.signature_provider_for(source: code, file: file)
|
|
605
|
+
elsif config.respond_to?(:signature_provider)
|
|
606
|
+
config.signature_provider
|
|
607
|
+
elsif config.respond_to?(:rbs_provider)
|
|
608
|
+
config.rbs_provider
|
|
463
609
|
end
|
|
610
|
+
rescue StandardError
|
|
611
|
+
config.respond_to?(:rbs_provider) ? config.rbs_provider : nil
|
|
464
612
|
end
|
|
465
613
|
|
|
466
|
-
# +Docscribe::InlineRewriter::Collector#extract_name_sym+ -> Object
|
|
467
|
-
#
|
|
468
614
|
# Method documentation.
|
|
469
615
|
#
|
|
470
616
|
# @private
|
|
471
|
-
# @param [Object]
|
|
617
|
+
# @param [Object] insertion Param documentation.
|
|
618
|
+
# @param [Object] config Param documentation.
|
|
619
|
+
# @param [Object] signature_provider Param documentation.
|
|
472
620
|
# @return [Object]
|
|
473
|
-
def
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
621
|
+
def build_method_doc(insertion, config:, signature_provider:)
|
|
622
|
+
DocBuilder.build(
|
|
623
|
+
insertion,
|
|
624
|
+
config: config,
|
|
625
|
+
signature_provider: signature_provider
|
|
626
|
+
)
|
|
478
627
|
end
|
|
479
628
|
|
|
480
|
-
# +Docscribe::InlineRewriter::Collector#self_node?+ -> Object
|
|
481
|
-
#
|
|
482
629
|
# Method documentation.
|
|
483
630
|
#
|
|
484
631
|
# @private
|
|
485
|
-
# @param [Object]
|
|
632
|
+
# @param [Object] insertion Param documentation.
|
|
633
|
+
# @param [Object] existing_lines Param documentation.
|
|
634
|
+
# @param [Object] config Param documentation.
|
|
635
|
+
# @param [Object] signature_provider Param documentation.
|
|
486
636
|
# @return [Object]
|
|
487
|
-
def
|
|
488
|
-
|
|
637
|
+
def build_missing_method_merge_result(insertion, existing_lines:, config:, signature_provider:)
|
|
638
|
+
DocBuilder.build_missing_merge_result(
|
|
639
|
+
insertion,
|
|
640
|
+
existing_lines: existing_lines,
|
|
641
|
+
config: config,
|
|
642
|
+
signature_provider: signature_provider
|
|
643
|
+
)
|
|
489
644
|
end
|
|
490
645
|
|
|
491
|
-
# +Docscribe::InlineRewriter::Collector#current_container+ -> Object
|
|
492
|
-
#
|
|
493
646
|
# Method documentation.
|
|
494
647
|
#
|
|
495
648
|
# @private
|
|
649
|
+
# @param [Object] buffer Param documentation.
|
|
650
|
+
# @param [Object] insertion Param documentation.
|
|
496
651
|
# @return [Object]
|
|
497
|
-
def
|
|
498
|
-
|
|
652
|
+
def method_doc_comment_info(buffer, insertion)
|
|
653
|
+
anchor_bol_range, def_bol_range = method_bol_ranges(buffer, insertion)
|
|
654
|
+
|
|
655
|
+
SourceHelpers.doc_comment_block_info(buffer, anchor_bol_range.begin_pos) ||
|
|
656
|
+
SourceHelpers.doc_comment_block_info(buffer, def_bol_range.begin_pos)
|
|
499
657
|
end
|
|
500
658
|
|
|
501
|
-
# +Docscribe::InlineRewriter::Collector#const_name+ -> Object
|
|
502
|
-
#
|
|
503
659
|
# Method documentation.
|
|
504
660
|
#
|
|
505
661
|
# @private
|
|
506
|
-
# @param [Object]
|
|
662
|
+
# @param [Object] buffer Param documentation.
|
|
663
|
+
# @param [Object] insertion Param documentation.
|
|
507
664
|
# @return [Object]
|
|
508
|
-
def
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
scope, name = *node
|
|
514
|
-
scope_name = scope ? const_name(scope) : nil
|
|
515
|
-
[scope_name, name].compact.join('::')
|
|
516
|
-
when :cbase
|
|
517
|
-
'' # leading ::
|
|
518
|
-
else
|
|
519
|
-
node.loc.expression.source # fallback
|
|
520
|
-
end
|
|
665
|
+
def method_comment_block_removal_range(buffer, insertion)
|
|
666
|
+
anchor_bol_range, def_bol_range = method_bol_ranges(buffer, insertion)
|
|
667
|
+
|
|
668
|
+
SourceHelpers.comment_block_removal_range(buffer, anchor_bol_range.begin_pos) ||
|
|
669
|
+
SourceHelpers.comment_block_removal_range(buffer, def_bol_range.begin_pos)
|
|
521
670
|
end
|
|
522
671
|
|
|
523
|
-
#
|
|
672
|
+
# Method documentation.
|
|
524
673
|
#
|
|
674
|
+
# @private
|
|
675
|
+
# @param [Object] buffer Param documentation.
|
|
676
|
+
# @param [Object] insertion Param documentation.
|
|
677
|
+
# @return [Array]
|
|
678
|
+
def method_bol_ranges(buffer, insertion)
|
|
679
|
+
anchor_node = anchor_node_for(insertion)
|
|
680
|
+
[
|
|
681
|
+
SourceHelpers.line_start_range(buffer, anchor_node),
|
|
682
|
+
SourceHelpers.line_start_range(buffer, insertion.node)
|
|
683
|
+
]
|
|
684
|
+
end
|
|
685
|
+
|
|
525
686
|
# Method documentation.
|
|
526
687
|
#
|
|
527
688
|
# @private
|
|
528
|
-
# @param [Object]
|
|
529
|
-
# @
|
|
689
|
+
# @param [Object] insertion Param documentation.
|
|
690
|
+
# @raise [StandardError]
|
|
530
691
|
# @return [Object]
|
|
531
|
-
|
|
532
|
-
|
|
692
|
+
# @return [Object] if StandardError
|
|
693
|
+
def method_line_for(insertion)
|
|
694
|
+
anchor_node_for(insertion).loc.expression.line
|
|
695
|
+
rescue StandardError
|
|
696
|
+
insertion.node.loc.expression.line
|
|
697
|
+
end
|
|
533
698
|
|
|
534
|
-
|
|
535
|
-
|
|
699
|
+
# Method documentation.
|
|
700
|
+
#
|
|
701
|
+
# @private
|
|
702
|
+
# @param [Object] insertion Param documentation.
|
|
703
|
+
# @return [Object]
|
|
704
|
+
def anchor_node_for(insertion)
|
|
705
|
+
if insertion.respond_to?(:anchor_node) && insertion.anchor_node
|
|
706
|
+
insertion.anchor_node
|
|
536
707
|
else
|
|
537
|
-
|
|
708
|
+
insertion.node
|
|
538
709
|
end
|
|
539
710
|
end
|
|
540
711
|
end
|