ruby-merge 7.0.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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/lib/ruby/merge/version.rb +11 -0
- data/lib/ruby/merge.rb +779 -0
- data/lib/ruby-merge.rb +3 -0
- data.tar.gz.sig +0 -0
- metadata +109 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3d9fb197110a8e0a388d9daf71e144696f7db240ff5f59bf9d6cecf10518950b
|
|
4
|
+
data.tar.gz: 01ea7a23152ec998ea17b1e4463a1ce7115e5b4463202c5ecd73ab1829a4a224
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ab2fe88791d7ec23b4b9e1525a0726638e3afa01af7136e92e67b89eaa821f290187b11d96885caf44fb51adffdb12e9f6da01c25bc333e1f2358b8f84b756f2
|
|
7
|
+
data.tar.gz: 301910a3b44cf21892fb23b1c25106ca1ac82f53481121bb921d7978bab9efa9ba574bc8c6ba574fdb51cf7166d8a550abde90e4b9efae76f0c22219e3af8531
|
checksums.yaml.gz.sig
ADDED
|
Binary file
|
data/lib/ruby/merge.rb
ADDED
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tree_haver"
|
|
4
|
+
require "ast/merge"
|
|
5
|
+
|
|
6
|
+
module Ruby
|
|
7
|
+
module Merge
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
PACKAGE_NAME = "ruby-merge"
|
|
11
|
+
TREE_SITTER_BACKEND = TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND
|
|
12
|
+
DESTINATION_WINS_ARRAY_POLICY = { surface: "array", name: "destination_wins_array" }.freeze
|
|
13
|
+
DIRECTIVE_LINE = /\A(?::nocov:|[\w-]+:(?:freeze|unfreeze))\z/
|
|
14
|
+
MAGIC_COMMENT_PREFIXES = %w[coding encoding frozen_string_literal shareable_constant_value typed warn_indent].freeze
|
|
15
|
+
REQUIRE_PATTERN = /^\s*require(?:_relative)?\s+["']([^"']+)["']/.freeze
|
|
16
|
+
DSL_CALL_PATTERN = /^(?<name>source|gemspec|git_source|gem|eval_gemfile|platform|group|desc|task)\b/.freeze
|
|
17
|
+
RAKEFILE_DEFAULT_TASK_COMMENT = "# Define a base default task early so other files can enhance it."
|
|
18
|
+
RAKEFILE_DEFAULT_TASK_DESC = 'desc "Default tasks aggregator"'
|
|
19
|
+
CLASS_PATTERN = /^\s*class\s+([A-Z]\w*(?:::\w+)*)/.freeze
|
|
20
|
+
MODULE_PATTERN = /^\s*module\s+([A-Z]\w*(?:::\w+)*)/.freeze
|
|
21
|
+
DEF_PATTERN = /^\s*def\s+(?:self\.)?([a-zA-Z_]\w*[!?=]?)/.freeze
|
|
22
|
+
EXAMPLE_TAG = /\A@example\b(?<rest>.*)\z/.freeze
|
|
23
|
+
TAG_PREFIX = /\A@[a-z_]+\b/.freeze
|
|
24
|
+
|
|
25
|
+
def ruby_feature_profile
|
|
26
|
+
{
|
|
27
|
+
family: "ruby",
|
|
28
|
+
supported_dialects: ["ruby"],
|
|
29
|
+
supported_policies: [DESTINATION_WINS_ARRAY_POLICY]
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def available_ruby_backends
|
|
34
|
+
[TREE_SITTER_BACKEND]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ruby_backend_feature_profile(backend: nil)
|
|
38
|
+
requested = backend.to_s.empty? ? TREE_SITTER_BACKEND.id : backend.to_s
|
|
39
|
+
return unsupported_feature_result("Unsupported Ruby backend #{requested}.") unless requested == TREE_SITTER_BACKEND.id
|
|
40
|
+
|
|
41
|
+
ruby_feature_profile.merge(
|
|
42
|
+
backend: requested,
|
|
43
|
+
backend_ref: TREE_SITTER_BACKEND.to_h,
|
|
44
|
+
supports_dialects: true
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def ruby_plan_context(backend: nil)
|
|
49
|
+
profile = ruby_backend_feature_profile(backend: backend)
|
|
50
|
+
return profile if profile[:ok] == false
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
family_profile: ruby_feature_profile,
|
|
54
|
+
feature_profile: {
|
|
55
|
+
backend: profile[:backend],
|
|
56
|
+
supports_dialects: true,
|
|
57
|
+
supported_policies: profile[:supported_policies]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def parse_ruby(source, dialect, backend: nil)
|
|
63
|
+
requested = backend.to_s.empty? ? TREE_SITTER_BACKEND.id : backend.to_s
|
|
64
|
+
return unsupported_feature_result("Unsupported Ruby dialect #{dialect}.") unless dialect == "ruby"
|
|
65
|
+
return unsupported_feature_result("Unsupported Ruby backend #{requested}.") unless requested == TREE_SITTER_BACKEND.id
|
|
66
|
+
|
|
67
|
+
syntax = TreeHaver.parse_with_language_pack(
|
|
68
|
+
TreeHaver::ParserRequest.new(source: source, language: "ruby", dialect: dialect)
|
|
69
|
+
)
|
|
70
|
+
return { ok: false, diagnostics: syntax[:diagnostics], policies: [] } unless syntax[:ok]
|
|
71
|
+
|
|
72
|
+
{
|
|
73
|
+
ok: true,
|
|
74
|
+
diagnostics: [],
|
|
75
|
+
analysis: analyze_ruby_document(source),
|
|
76
|
+
policies: []
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def match_ruby_owners(template, destination)
|
|
81
|
+
destination_paths = destination[:owners].to_h { |owner| [owner[:path], true] }
|
|
82
|
+
template_paths = template[:owners].to_h { |owner| [owner[:path], true] }
|
|
83
|
+
{
|
|
84
|
+
matched: template[:owners]
|
|
85
|
+
.filter { |owner| destination_paths[owner[:path]] }
|
|
86
|
+
.map { |owner| { template_path: owner[:path], destination_path: owner[:path] } },
|
|
87
|
+
unmatched_template: template[:owners].map { |owner| owner[:path] }.reject { |path| destination_paths[path] },
|
|
88
|
+
unmatched_destination: destination[:owners].map { |owner| owner[:path] }.reject { |path| template_paths[path] }
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def merge_ruby(template_source, destination_source, dialect, merge_template_requires: false)
|
|
93
|
+
template = parse_ruby(template_source, dialect)
|
|
94
|
+
return template unless template[:ok]
|
|
95
|
+
|
|
96
|
+
destination = parse_ruby(destination_source, dialect)
|
|
97
|
+
unless destination[:ok]
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
diagnostics: destination[:diagnostics].map do |diagnostic|
|
|
101
|
+
diagnostic[:category] == "parse_error" ? diagnostic.merge(category: "destination_parse_error") : diagnostic
|
|
102
|
+
end,
|
|
103
|
+
policies: []
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
destination_requires = collect_ruby_require_entries(destination.dig(:analysis, :source))
|
|
108
|
+
template_requires = collect_ruby_require_entries(template.dig(:analysis, :source))
|
|
109
|
+
destination_declarations = collect_ruby_declaration_entries(destination.dig(:analysis, :source))
|
|
110
|
+
template_declarations = collect_ruby_declaration_entries(template.dig(:analysis, :source))
|
|
111
|
+
destination_paths = destination_declarations.to_h { |entry| [entry[:path], true] }
|
|
112
|
+
destination_dsl = collect_top_level_dsl_entries(destination.dig(:analysis, :source))
|
|
113
|
+
template_dsl = collect_top_level_dsl_entries(template.dig(:analysis, :source))
|
|
114
|
+
sections = []
|
|
115
|
+
preamble = collect_ruby_preamble(destination.dig(:analysis, :source))
|
|
116
|
+
sections << preamble unless preamble.empty?
|
|
117
|
+
requires = merge_template_requires ? merge_ruby_requires(destination_requires, template_requires) : destination_requires
|
|
118
|
+
require_block = requires.map { |entry| entry[:text] }.join("\n").strip
|
|
119
|
+
sections << require_block unless require_block.empty?
|
|
120
|
+
sections.concat(merge_top_level_dsl_entries(destination_dsl, template_dsl).map { |entry| entry[:text] })
|
|
121
|
+
sections.concat(destination_declarations.map { |entry| entry[:text] })
|
|
122
|
+
sections.concat(
|
|
123
|
+
template_declarations.reject { |entry| destination_paths[entry[:path]] }.map { |entry| entry[:text] }
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
output = "#{sections.join("\n\n").strip}\n"
|
|
127
|
+
|
|
128
|
+
{
|
|
129
|
+
ok: true,
|
|
130
|
+
diagnostics: [],
|
|
131
|
+
output: normalize_rakefile_default_task_scaffold(output),
|
|
132
|
+
policies: [DESTINATION_WINS_ARRAY_POLICY]
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def ruby_discovered_surfaces(analysis)
|
|
137
|
+
analysis[:discovered_surfaces] || []
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def ruby_delegated_child_operations(analysis, parent_operation_id: "ruby-document-0")
|
|
141
|
+
surfaces = ruby_discovered_surfaces(analysis)
|
|
142
|
+
doc_operation_ids = {}
|
|
143
|
+
operations = []
|
|
144
|
+
|
|
145
|
+
surfaces.each_with_index do |surface, index|
|
|
146
|
+
next unless surface[:surface_kind] == "ruby_doc_comment"
|
|
147
|
+
|
|
148
|
+
operation_id = "ruby-doc-comment-#{index}"
|
|
149
|
+
doc_operation_ids[surface[:address]] = operation_id
|
|
150
|
+
operations << Ast::Merge.delegated_child_operation(
|
|
151
|
+
operation_id: operation_id,
|
|
152
|
+
parent_operation_id: parent_operation_id,
|
|
153
|
+
requested_strategy: "delegate_child_surface",
|
|
154
|
+
language_chain: ["ruby", surface[:effective_language]],
|
|
155
|
+
surface: surface
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
example_index = 0
|
|
160
|
+
surfaces.each do |surface|
|
|
161
|
+
next unless surface[:surface_kind] == "yard_example_block"
|
|
162
|
+
|
|
163
|
+
operations << Ast::Merge.delegated_child_operation(
|
|
164
|
+
operation_id: "yard-example-#{example_index}",
|
|
165
|
+
parent_operation_id: doc_operation_ids.fetch(surface[:parent_address], parent_operation_id),
|
|
166
|
+
requested_strategy: "delegate_child_surface",
|
|
167
|
+
language_chain: ["ruby", "yard", surface[:effective_language]],
|
|
168
|
+
surface: surface
|
|
169
|
+
)
|
|
170
|
+
example_index += 1
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
operations
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def apply_ruby_delegated_child_outputs(source, delegated_operations, apply_plan, applied_children)
|
|
177
|
+
lines = normalize_source(source).split("\n")
|
|
178
|
+
operations_by_id = delegated_operations.to_h { |operation| [operation[:operation_id], operation] }
|
|
179
|
+
outputs_by_id = applied_children.to_h { |entry| [entry[:operation_id], entry[:output]] }
|
|
180
|
+
|
|
181
|
+
replacements = apply_plan[:entries].filter_map do |entry|
|
|
182
|
+
operation = operations_by_id[entry.dig(:delegated_group, :child_operation_id)]
|
|
183
|
+
output = outputs_by_id[entry.dig(:delegated_group, :child_operation_id)]
|
|
184
|
+
span = operation&.dig(:surface, :span)
|
|
185
|
+
next if operation.nil? || output.nil? || span.nil?
|
|
186
|
+
|
|
187
|
+
{ start: span[:start_line] - 1, finish: span[:end_line] - 1, output: output }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
replacements.sort_by { |entry| -entry[:start] }.each do |entry|
|
|
191
|
+
prefix = comment_prefix_for(lines[entry[:start]])
|
|
192
|
+
replacement_lines = entry[:output].empty? ? [] : entry[:output].sub(/\n\z/, "").split("\n").map { |line| "#{prefix}#{line}" }
|
|
193
|
+
lines[entry[:start]..entry[:finish]] = replacement_lines
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
{
|
|
197
|
+
ok: true,
|
|
198
|
+
diagnostics: [],
|
|
199
|
+
output: "#{lines.join("\n").sub(/\n+\z/, "")}\n",
|
|
200
|
+
policies: [DESTINATION_WINS_ARRAY_POLICY]
|
|
201
|
+
}
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def merge_ruby_with_nested_outputs(template_source, destination_source, dialect, nested_outputs)
|
|
205
|
+
Ast::Merge.execute_nested_merge(
|
|
206
|
+
nested_outputs,
|
|
207
|
+
default_family: "ruby",
|
|
208
|
+
request_id_prefix: "nested_ruby_child",
|
|
209
|
+
merge_parent: -> { merge_ruby(template_source, destination_source, dialect) },
|
|
210
|
+
discover_operations: lambda { |merged_output|
|
|
211
|
+
analysis = parse_ruby(merged_output, dialect)
|
|
212
|
+
next { ok: false, diagnostics: analysis[:diagnostics] || [] } unless analysis[:ok]
|
|
213
|
+
|
|
214
|
+
{
|
|
215
|
+
ok: true,
|
|
216
|
+
diagnostics: [],
|
|
217
|
+
operations: ruby_delegated_child_operations(analysis[:analysis])
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
apply_resolved_outputs: lambda { |merged_output, operations, apply_plan, applied_children|
|
|
221
|
+
apply_ruby_delegated_child_outputs(
|
|
222
|
+
merged_output,
|
|
223
|
+
operations,
|
|
224
|
+
apply_plan,
|
|
225
|
+
applied_children
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def merge_ruby_with_reviewed_nested_outputs(template_source, destination_source, dialect, review_state, applied_children)
|
|
232
|
+
Ast::Merge.execute_reviewed_nested_merge(
|
|
233
|
+
review_state,
|
|
234
|
+
"ruby",
|
|
235
|
+
applied_children,
|
|
236
|
+
merge_parent: -> { merge_ruby(template_source, destination_source, dialect) },
|
|
237
|
+
discover_operations: lambda { |merged_output|
|
|
238
|
+
analysis = parse_ruby(merged_output, dialect)
|
|
239
|
+
next({ ok: false, diagnostics: analysis[:diagnostics] || [] }) unless analysis[:ok]
|
|
240
|
+
|
|
241
|
+
{
|
|
242
|
+
ok: true,
|
|
243
|
+
diagnostics: [],
|
|
244
|
+
operations: ruby_delegated_child_operations(analysis[:analysis])
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
apply_resolved_outputs: lambda { |merged_output, operations, apply_plan, resolved_children|
|
|
248
|
+
apply_ruby_delegated_child_outputs(
|
|
249
|
+
merged_output,
|
|
250
|
+
operations,
|
|
251
|
+
apply_plan,
|
|
252
|
+
resolved_children
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def merge_ruby_with_reviewed_nested_outputs_from_replay_bundle(template_source, destination_source, dialect, replay_bundle)
|
|
259
|
+
execution = Array(replay_bundle[:reviewed_nested_executions]).find { |entry| entry[:family] == "ruby" }
|
|
260
|
+
return { ok: false, diagnostics: [{ severity: "error", category: "configuration_error", message: "review replay bundle does not include a reviewed nested execution for ruby." }], policies: [] } unless execution
|
|
261
|
+
|
|
262
|
+
merge_ruby_with_reviewed_nested_outputs(
|
|
263
|
+
template_source,
|
|
264
|
+
destination_source,
|
|
265
|
+
dialect,
|
|
266
|
+
execution[:review_state],
|
|
267
|
+
execution[:applied_children]
|
|
268
|
+
)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def merge_ruby_with_reviewed_nested_outputs_from_review_state(template_source, destination_source, dialect, review_state)
|
|
272
|
+
execution = Array(review_state[:reviewed_nested_executions]).find { |entry| entry[:family] == "ruby" }
|
|
273
|
+
return { ok: false, diagnostics: [{ severity: "error", category: "configuration_error", message: "review state does not include a reviewed nested execution for ruby." }], policies: [] } unless execution
|
|
274
|
+
|
|
275
|
+
merge_ruby_with_reviewed_nested_outputs(
|
|
276
|
+
template_source,
|
|
277
|
+
destination_source,
|
|
278
|
+
dialect,
|
|
279
|
+
execution[:review_state],
|
|
280
|
+
execution[:applied_children]
|
|
281
|
+
)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def merge_ruby_with_reviewed_nested_outputs_from_replay_bundle_envelope(template_source, destination_source, dialect, envelope)
|
|
285
|
+
replay_bundle, import_error = Ast::Merge.import_review_replay_bundle_envelope(envelope)
|
|
286
|
+
return { ok: false, diagnostics: [{ severity: "error", category: import_error[:category], message: import_error[:message] }], policies: [] } if import_error
|
|
287
|
+
|
|
288
|
+
merge_ruby_with_reviewed_nested_outputs_from_replay_bundle(
|
|
289
|
+
template_source,
|
|
290
|
+
destination_source,
|
|
291
|
+
dialect,
|
|
292
|
+
replay_bundle
|
|
293
|
+
)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def merge_ruby_with_reviewed_nested_outputs_from_review_state_envelope(template_source, destination_source, dialect, envelope)
|
|
297
|
+
review_state, import_error = Ast::Merge.import_conformance_manifest_review_state_envelope(envelope)
|
|
298
|
+
return { ok: false, diagnostics: [{ severity: "error", category: import_error[:category], message: import_error[:message] }], policies: [] } if import_error
|
|
299
|
+
|
|
300
|
+
merge_ruby_with_reviewed_nested_outputs_from_review_state(
|
|
301
|
+
template_source,
|
|
302
|
+
destination_source,
|
|
303
|
+
dialect,
|
|
304
|
+
review_state
|
|
305
|
+
)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def analyze_ruby_document(source)
|
|
309
|
+
lines = normalize_source(source).split("\n", -1)
|
|
310
|
+
requires = []
|
|
311
|
+
declarations = []
|
|
312
|
+
discovered_surfaces = []
|
|
313
|
+
pending_comments = []
|
|
314
|
+
|
|
315
|
+
lines.each_with_index do |line, index|
|
|
316
|
+
line_number = index + 1
|
|
317
|
+
stripped = line.strip
|
|
318
|
+
|
|
319
|
+
if comment_line?(line)
|
|
320
|
+
pending_comments << { line: line_number, raw: line }
|
|
321
|
+
next
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
if stripped.empty?
|
|
325
|
+
pending_comments = []
|
|
326
|
+
next
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
if (match = REQUIRE_PATTERN.match(line))
|
|
330
|
+
requires << {
|
|
331
|
+
path: "/requires/#{requires.length}",
|
|
332
|
+
owner_kind: "require",
|
|
333
|
+
match_key: match[1]
|
|
334
|
+
}
|
|
335
|
+
pending_comments = []
|
|
336
|
+
next
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
declaration = declaration_for_line(line)
|
|
340
|
+
if declaration
|
|
341
|
+
declarations << {
|
|
342
|
+
path: "/declarations/#{declaration[:name]}",
|
|
343
|
+
owner_kind: "declaration",
|
|
344
|
+
match_key: declaration[:name]
|
|
345
|
+
}
|
|
346
|
+
surfaces = surfaces_for_owner(
|
|
347
|
+
owner_name: declaration[:name],
|
|
348
|
+
comment_entries: pending_comments
|
|
349
|
+
)
|
|
350
|
+
discovered_surfaces.concat(surfaces)
|
|
351
|
+
pending_comments = []
|
|
352
|
+
next
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
pending_comments = []
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
{
|
|
359
|
+
kind: "ruby",
|
|
360
|
+
dialect: "ruby",
|
|
361
|
+
root_kind: "document",
|
|
362
|
+
source: normalize_source(source),
|
|
363
|
+
owners: (requires + declarations).sort_by { |owner| owner[:path] },
|
|
364
|
+
discovered_surfaces: discovered_surfaces
|
|
365
|
+
}
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def collect_ruby_require_entries(source)
|
|
369
|
+
normalize_source(source).split("\n").filter_map do |line|
|
|
370
|
+
match = REQUIRE_PATTERN.match(line)
|
|
371
|
+
next unless match
|
|
372
|
+
|
|
373
|
+
{ path: "/requires/#{match[1]}", text: line.rstrip }
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def collect_ruby_preamble(source)
|
|
378
|
+
lines = normalize_source(source).split("\n")
|
|
379
|
+
preamble = []
|
|
380
|
+
lines.each do |line|
|
|
381
|
+
break unless line.strip.empty? || comment_line?(line)
|
|
382
|
+
|
|
383
|
+
preamble << line.rstrip
|
|
384
|
+
end
|
|
385
|
+
preamble.join("\n").strip
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def collect_top_level_dsl_entries(source)
|
|
389
|
+
lines = normalize_source(source).split("\n")
|
|
390
|
+
entries = []
|
|
391
|
+
pending_comments = []
|
|
392
|
+
index = 0
|
|
393
|
+
|
|
394
|
+
while index < lines.length
|
|
395
|
+
line = lines[index]
|
|
396
|
+
stripped = line.strip
|
|
397
|
+
if comment_line?(line)
|
|
398
|
+
pending_comments << index
|
|
399
|
+
index += 1
|
|
400
|
+
next
|
|
401
|
+
end
|
|
402
|
+
if stripped.empty?
|
|
403
|
+
pending_comments = []
|
|
404
|
+
index += 1
|
|
405
|
+
next
|
|
406
|
+
end
|
|
407
|
+
if REQUIRE_PATTERN.match?(line) || declaration_for_line(line)
|
|
408
|
+
pending_comments = []
|
|
409
|
+
index += 1
|
|
410
|
+
next
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
if line.match?(/\Abegin\b/)
|
|
414
|
+
start_index = pending_comments.first || index
|
|
415
|
+
finish_index = ruby_block_finish_index(lines, index)
|
|
416
|
+
text = lines[start_index..finish_index].join("\n").strip
|
|
417
|
+
signature = begin_block_signature(text)
|
|
418
|
+
entries << { path: "/dsl/#{signature}", name: "begin", signature: signature, text: text }
|
|
419
|
+
pending_comments = []
|
|
420
|
+
index = finish_index + 1
|
|
421
|
+
next
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
match = DSL_CALL_PATTERN.match(line)
|
|
425
|
+
unless match
|
|
426
|
+
pending_comments = []
|
|
427
|
+
index += 1
|
|
428
|
+
next
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
name = match[:name]
|
|
432
|
+
if name == "desc" && next_code_line_is_task?(lines, index + 1)
|
|
433
|
+
pending_comments << index
|
|
434
|
+
index += 1
|
|
435
|
+
next
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
start_index = pending_comments.first || index
|
|
439
|
+
finish_index = dsl_entry_finish_index(lines, index)
|
|
440
|
+
text = lines[start_index..finish_index].join("\n").strip
|
|
441
|
+
signature = dsl_entry_signature(name, line)
|
|
442
|
+
entries << { path: "/dsl/#{signature}", name: name, signature: signature, text: text } if signature
|
|
443
|
+
pending_comments = []
|
|
444
|
+
index = finish_index + 1
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
entries
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def merge_top_level_dsl_entries(destination_entries, template_entries)
|
|
451
|
+
destination_by_signature = destination_entries.to_h { |entry| [entry[:signature], entry] }
|
|
452
|
+
template_singletons = template_entries.select { |entry| dsl_singleton_entry?(entry) }
|
|
453
|
+
template_singleton_signatures = template_singletons.map { |entry| entry[:signature] }.to_h { |signature| [signature, true] }
|
|
454
|
+
result = []
|
|
455
|
+
result.concat(template_singletons)
|
|
456
|
+
result.concat(destination_entries.reject { |entry| template_singleton_signatures[entry[:signature]] })
|
|
457
|
+
result.concat(
|
|
458
|
+
template_entries.reject do |entry|
|
|
459
|
+
dsl_singleton_entry?(entry) || destination_by_signature[entry[:signature]]
|
|
460
|
+
end
|
|
461
|
+
)
|
|
462
|
+
result
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def merge_ruby_requires(destination_requires, template_requires)
|
|
466
|
+
destination_paths = destination_requires.to_h { |entry| [entry[:path], true] }
|
|
467
|
+
destination_requires + template_requires.reject { |entry| destination_paths[entry[:path]] }
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def collect_ruby_declaration_entries(source)
|
|
471
|
+
lines = normalize_source(source).split("\n")
|
|
472
|
+
entries = []
|
|
473
|
+
pending_comments = []
|
|
474
|
+
index = 0
|
|
475
|
+
|
|
476
|
+
while index < lines.length
|
|
477
|
+
line = lines[index]
|
|
478
|
+
stripped = line.strip
|
|
479
|
+
|
|
480
|
+
if comment_line?(line)
|
|
481
|
+
pending_comments << index
|
|
482
|
+
index += 1
|
|
483
|
+
next
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
if stripped.empty?
|
|
487
|
+
pending_comments = []
|
|
488
|
+
index += 1
|
|
489
|
+
next
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
if REQUIRE_PATTERN.match?(line)
|
|
493
|
+
pending_comments = []
|
|
494
|
+
index += 1
|
|
495
|
+
next
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
declaration = declaration_for_line(line)
|
|
499
|
+
unless declaration
|
|
500
|
+
pending_comments = []
|
|
501
|
+
index += 1
|
|
502
|
+
next
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
start_index = pending_comments.first || index
|
|
506
|
+
depth = 1
|
|
507
|
+
cursor = index + 1
|
|
508
|
+
while cursor < lines.length
|
|
509
|
+
candidate = lines[cursor].strip
|
|
510
|
+
depth += 1 if declaration_for_line(candidate)
|
|
511
|
+
if candidate == "end"
|
|
512
|
+
depth -= 1
|
|
513
|
+
if depth.zero?
|
|
514
|
+
cursor += 1
|
|
515
|
+
break
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
cursor += 1
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
entries << {
|
|
522
|
+
path: "/declarations/#{declaration[:name]}",
|
|
523
|
+
text: lines[start_index...cursor].join("\n").strip
|
|
524
|
+
}
|
|
525
|
+
pending_comments = []
|
|
526
|
+
index = cursor
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
entries
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def unsupported_feature_result(message)
|
|
533
|
+
{
|
|
534
|
+
ok: false,
|
|
535
|
+
diagnostics: [{ severity: "error", category: "unsupported_feature", message: message }],
|
|
536
|
+
policies: []
|
|
537
|
+
}
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
private
|
|
541
|
+
|
|
542
|
+
def comment_line?(line)
|
|
543
|
+
line.lstrip.start_with?("#")
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def declaration_for_line(line)
|
|
547
|
+
if (match = CLASS_PATTERN.match(line))
|
|
548
|
+
{ kind: "class", name: match[1] }
|
|
549
|
+
elsif (match = MODULE_PATTERN.match(line))
|
|
550
|
+
{ kind: "module", name: match[1] }
|
|
551
|
+
elsif (match = DEF_PATTERN.match(line))
|
|
552
|
+
{ kind: "def", name: match[1] }
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def next_code_line_is_task?(lines, start_index)
|
|
557
|
+
lines[start_index..].to_a.each do |line|
|
|
558
|
+
next if line.strip.empty? || comment_line?(line)
|
|
559
|
+
|
|
560
|
+
match = DSL_CALL_PATTERN.match(line)
|
|
561
|
+
return match && match[:name] == "task"
|
|
562
|
+
end
|
|
563
|
+
false
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
def dsl_entry_finish_index(lines, start_index)
|
|
567
|
+
return start_index unless lines[start_index].match?(/\bdo\b/)
|
|
568
|
+
|
|
569
|
+
ruby_block_finish_index(lines, start_index)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def ruby_block_finish_index(lines, start_index)
|
|
573
|
+
depth = 0
|
|
574
|
+
cursor = start_index
|
|
575
|
+
while cursor < lines.length
|
|
576
|
+
stripped = lines[cursor].strip
|
|
577
|
+
depth += stripped.scan(/\bdo\b/).length
|
|
578
|
+
depth += 1 if declaration_for_line(stripped) || stripped.match?(/\A(begin|if|unless|case|while|until|for)\b/)
|
|
579
|
+
depth -= 1 if stripped == "end"
|
|
580
|
+
return cursor if depth <= 0 && cursor > start_index
|
|
581
|
+
|
|
582
|
+
cursor += 1
|
|
583
|
+
end
|
|
584
|
+
lines.length - 1
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
def begin_block_signature(text)
|
|
588
|
+
require_path = text[/^\s*require(?:_relative)?\s+["']([^"']+)["']/, 1]
|
|
589
|
+
return "begin:require:#{require_path}" if require_path
|
|
590
|
+
|
|
591
|
+
"begin:#{text.lines.first.to_s.strip}"
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def dsl_entry_signature(name, line)
|
|
595
|
+
case name
|
|
596
|
+
when "source", "gemspec"
|
|
597
|
+
name
|
|
598
|
+
when "git_source", "gem", "eval_gemfile", "platform", "group", "task"
|
|
599
|
+
first_argument = line[/\b#{Regexp.escape(name)}\s*(?:\(|\s)\s*["']([^"']+)["']/, 1] ||
|
|
600
|
+
line[/\b#{Regexp.escape(name)}\s*(?:\(|\s)\s*:([a-zA-Z_]\w*[!?=]?)/, 1]
|
|
601
|
+
first_argument ? "#{name}:#{normalize_dsl_argument(name, first_argument)}" : "#{name}:#{line.strip}"
|
|
602
|
+
when "desc"
|
|
603
|
+
"desc:#{line.strip}"
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def normalize_dsl_argument(name, argument)
|
|
608
|
+
return argument.gsub(%r{/r\d+/}, "/") if name == "eval_gemfile"
|
|
609
|
+
|
|
610
|
+
argument
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def dsl_singleton_entry?(entry)
|
|
614
|
+
%w[source gemspec].include?(entry[:name])
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def normalize_rakefile_default_task_scaffold(content)
|
|
618
|
+
lines = normalize_source(content).split("\n")
|
|
619
|
+
desc_index = lines.find_index { |line| line.strip == RAKEFILE_DEFAULT_TASK_DESC }
|
|
620
|
+
return content unless desc_index
|
|
621
|
+
|
|
622
|
+
comment_index = preceding_code_line_index(lines, desc_index - 1)
|
|
623
|
+
return content unless comment_index && lines[comment_index].strip == RAKEFILE_DEFAULT_TASK_COMMENT
|
|
624
|
+
|
|
625
|
+
next_code_index = next_code_line_index(lines, desc_index + 1)
|
|
626
|
+
return content if next_code_index && lines[next_code_index].match?(/\Atask\s+:default\b/)
|
|
627
|
+
|
|
628
|
+
task_index = lines.each_index.find { |index| lines[index].match?(/\Atask\s+:default\b/) }
|
|
629
|
+
return content unless task_index
|
|
630
|
+
|
|
631
|
+
finish_index = dsl_entry_finish_index(lines, task_index)
|
|
632
|
+
task_block = lines[task_index..finish_index]
|
|
633
|
+
lines[task_index..finish_index] = []
|
|
634
|
+
insertion_index = lines.find_index { |line| line.strip == RAKEFILE_DEFAULT_TASK_DESC } + 1
|
|
635
|
+
insertion = task_block.dup
|
|
636
|
+
insertion << "" unless lines[insertion_index].to_s.strip.empty?
|
|
637
|
+
lines.insert(insertion_index, *insertion)
|
|
638
|
+
"#{lines.join("\n").sub(/\n+\z/, "")}\n"
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def preceding_code_line_index(lines, start_index)
|
|
642
|
+
start_index.downto(0) do |index|
|
|
643
|
+
next if lines[index].strip.empty?
|
|
644
|
+
|
|
645
|
+
return index
|
|
646
|
+
end
|
|
647
|
+
nil
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
def next_code_line_index(lines, start_index)
|
|
651
|
+
start_index.upto(lines.length - 1) do |index|
|
|
652
|
+
next if lines[index].strip.empty?
|
|
653
|
+
|
|
654
|
+
return index
|
|
655
|
+
end
|
|
656
|
+
nil
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
def surfaces_for_owner(owner_name:, comment_entries:)
|
|
660
|
+
filtered_entries = comment_entries.filter { |entry| doc_comment_content?(entry[:raw]) }
|
|
661
|
+
return [] if filtered_entries.empty?
|
|
662
|
+
|
|
663
|
+
start_line = filtered_entries.first[:line]
|
|
664
|
+
end_line = filtered_entries.last[:line]
|
|
665
|
+
doc_surface = Ast::Merge.discovered_surface(
|
|
666
|
+
surface_kind: "ruby_doc_comment",
|
|
667
|
+
declared_language: "yard",
|
|
668
|
+
effective_language: "yard",
|
|
669
|
+
address: "document[0] > ruby_doc_comment[#{owner_name}]",
|
|
670
|
+
parent_address: "document[0]",
|
|
671
|
+
owner: Ast::Merge.surface_owner_ref(kind: "owned_region", address: "/declarations/#{owner_name}"),
|
|
672
|
+
span: Ast::Merge.surface_span(start_line: start_line, end_line: end_line),
|
|
673
|
+
reconstruction_strategy: "rewrite_with_prefix_preservation",
|
|
674
|
+
metadata: {
|
|
675
|
+
owner_signature: owner_name,
|
|
676
|
+
comment_prefix: comment_prefix_for(filtered_entries.first[:raw]),
|
|
677
|
+
entries: filtered_entries.map { |entry| { line: entry[:line], raw: entry[:raw] } }
|
|
678
|
+
}
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
[doc_surface] + example_surfaces_for(doc_surface)
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
def example_surfaces_for(surface)
|
|
685
|
+
entries = Array(surface.dig(:metadata, :entries))
|
|
686
|
+
normalized = entries.map { |entry| normalize_comment_content(entry[:raw]) }
|
|
687
|
+
|
|
688
|
+
normalized.each_with_index.filter_map do |content, tag_index|
|
|
689
|
+
match = EXAMPLE_TAG.match(content)
|
|
690
|
+
next unless match
|
|
691
|
+
|
|
692
|
+
body_start = tag_index + 1
|
|
693
|
+
body_end = next_tag_index(normalized, body_start) || normalized.length
|
|
694
|
+
next if body_start >= body_end
|
|
695
|
+
|
|
696
|
+
body_entries = entries[body_start...body_end]
|
|
697
|
+
next if body_entries.nil? || body_entries.empty?
|
|
698
|
+
|
|
699
|
+
declared_language = declared_example_language(match[:rest]) || "ruby"
|
|
700
|
+
Ast::Merge.discovered_surface(
|
|
701
|
+
surface_kind: "yard_example_block",
|
|
702
|
+
declared_language: declared_language,
|
|
703
|
+
effective_language: declared_language,
|
|
704
|
+
address: "#{surface[:address]} > yard_example[#{tag_index}]",
|
|
705
|
+
parent_address: surface[:address],
|
|
706
|
+
owner: Ast::Merge.surface_owner_ref(kind: "owned_region", address: surface[:address]),
|
|
707
|
+
span: Ast::Merge.surface_span(start_line: body_entries.first[:line], end_line: body_entries.last[:line]),
|
|
708
|
+
reconstruction_strategy: "rewrite_with_prefix_preservation",
|
|
709
|
+
metadata: {
|
|
710
|
+
tag_kind: "example",
|
|
711
|
+
tag_index: tag_index,
|
|
712
|
+
tag_text: normalized[tag_index],
|
|
713
|
+
comment_prefix: surface.dig(:metadata, :comment_prefix)
|
|
714
|
+
}
|
|
715
|
+
)
|
|
716
|
+
end
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
def next_tag_index(normalized_lines, start_index)
|
|
720
|
+
normalized_lines.each_with_index do |content, index|
|
|
721
|
+
next if index < start_index
|
|
722
|
+
return index if TAG_PREFIX.match?(content)
|
|
723
|
+
end
|
|
724
|
+
nil
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
def normalize_source(source)
|
|
728
|
+
source.gsub(/\r\n?/, "\n")
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
def normalize_comment_content(raw)
|
|
732
|
+
raw.to_s.sub(/\A\s*#\s?/, "").strip
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
def doc_comment_content?(raw)
|
|
736
|
+
content = normalize_comment_content(raw)
|
|
737
|
+
return false if content.empty?
|
|
738
|
+
return false if DIRECTIVE_LINE.match?(content)
|
|
739
|
+
return false if MAGIC_COMMENT_PREFIXES.any? { |prefix| content.start_with?("#{prefix}:") }
|
|
740
|
+
|
|
741
|
+
true
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
def comment_prefix_for(raw)
|
|
745
|
+
raw.to_s[/\A\s*#\s*/] || "# "
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
def declared_example_language(rest)
|
|
749
|
+
match = rest.to_s.strip.match(/\A\[(?<language>[^\]]+)\]/)
|
|
750
|
+
language = match && match[:language]
|
|
751
|
+
return if language.nil? || language.empty?
|
|
752
|
+
|
|
753
|
+
language.downcase.tr("-", "_")
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
module_function(
|
|
757
|
+
:ruby_feature_profile,
|
|
758
|
+
:available_ruby_backends,
|
|
759
|
+
:ruby_backend_feature_profile,
|
|
760
|
+
:ruby_plan_context,
|
|
761
|
+
:parse_ruby,
|
|
762
|
+
:match_ruby_owners,
|
|
763
|
+
:merge_ruby,
|
|
764
|
+
:ruby_discovered_surfaces,
|
|
765
|
+
:ruby_delegated_child_operations,
|
|
766
|
+
:apply_ruby_delegated_child_outputs,
|
|
767
|
+
:merge_ruby_with_reviewed_nested_outputs,
|
|
768
|
+
:merge_ruby_with_reviewed_nested_outputs_from_replay_bundle,
|
|
769
|
+
:merge_ruby_with_reviewed_nested_outputs_from_replay_bundle_envelope,
|
|
770
|
+
:merge_ruby_with_reviewed_nested_outputs_from_review_state,
|
|
771
|
+
:merge_ruby_with_reviewed_nested_outputs_from_review_state_envelope,
|
|
772
|
+
:merge_ruby_with_nested_outputs,
|
|
773
|
+
:analyze_ruby_document,
|
|
774
|
+
:collect_ruby_require_entries,
|
|
775
|
+
:collect_ruby_declaration_entries,
|
|
776
|
+
:unsupported_feature_result
|
|
777
|
+
)
|
|
778
|
+
end
|
|
779
|
+
end
|
data/lib/ruby-merge.rb
ADDED
data.tar.gz.sig
ADDED
|
Binary file
|
metadata
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruby-merge
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 7.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Peter H. Boling
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain:
|
|
10
|
+
- |
|
|
11
|
+
-----BEGIN CERTIFICATE-----
|
|
12
|
+
MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl
|
|
13
|
+
ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW
|
|
14
|
+
A2NvbTAeFw0yNTA1MDQxNTMzMDlaFw00NTA0MjkxNTMzMDlaMEMxFTATBgNVBAMM
|
|
15
|
+
DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
|
|
16
|
+
LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAruUoo0WA
|
|
17
|
+
uoNuq6puKWYeRYiZekz/nsDeK5x/0IEirzcCEvaHr3Bmz7rjo1I6On3gGKmiZs61
|
|
18
|
+
LRmQ3oxy77ydmkGTXBjruJB+pQEn7UfLSgQ0xa1/X3kdBZt6RmabFlBxnHkoaGY5
|
|
19
|
+
mZuZ5+Z7walmv6sFD9ajhzj+oIgwWfnEHkXYTR8I6VLN7MRRKGMPoZ/yvOmxb2DN
|
|
20
|
+
coEEHWKO9CvgYpW7asIihl/9GMpKiRkcYPm9dGQzZc6uTwom1COfW0+ZOFrDVBuV
|
|
21
|
+
FMQRPswZcY4Wlq0uEBLPU7hxnCL9nKK6Y9IhdDcz1mY6HZ91WImNslOSI0S8hRpj
|
|
22
|
+
yGOWxQIhBT3fqCBlRIqFQBudrnD9jSNpSGsFvbEijd5ns7Z9ZMehXkXDycpGAUj1
|
|
23
|
+
to/5cuTWWw1JqUWrKJYoifnVhtE1o1DZ+LkPtWxHtz5kjDG/zR3MG0Ula0UOavlD
|
|
24
|
+
qbnbcXPBnwXtTFeZ3C+yrWpE4pGnl3yGkZj9SMTlo9qnTMiPmuWKQDatAgMBAAGj
|
|
25
|
+
fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQE8uWvNbPVNRXZ
|
|
26
|
+
HlgPbc2PCzC4bjAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG
|
|
27
|
+
A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD
|
|
28
|
+
ggGBAJbnUwfJQFPkBgH9cL7hoBfRtmWiCvdqdjeTmi04u8zVNCUox0A4gT982DE9
|
|
29
|
+
wmuN12LpdajxZONqbXuzZvc+nb0StFwmFYZG6iDwaf4BPywm2e/Vmq0YG45vZXGR
|
|
30
|
+
L8yMDSK1cQXjmA+ZBKOHKWavxP6Vp7lWvjAhz8RFwqF9GuNIdhv9NpnCAWcMZtpm
|
|
31
|
+
GUPyIWw/Cw/2wZp74QzZj6Npx+LdXoLTF1HMSJXZ7/pkxLCsB8m4EFVdb/IrW/0k
|
|
32
|
+
kNSfjtAfBHO8nLGuqQZVH9IBD1i9K6aSs7pT6TW8itXUIlkIUI2tg5YzW6OFfPzq
|
|
33
|
+
QekSkX3lZfY+HTSp/o+YvKkqWLUV7PQ7xh1ZYDtocpaHwgxe/j3bBqHE+CUPH2vA
|
|
34
|
+
0V/FwdTRWcwsjVoOJTrYcff8pBZ8r2MvtAc54xfnnhGFzeRHfcltobgFxkAXdE6p
|
|
35
|
+
DVjBtqT23eugOqQ73umLcYDZkc36vnqGxUBSsXrzY9pzV5gGr2I8YUxMqf6ATrZt
|
|
36
|
+
L9nRqA==
|
|
37
|
+
-----END CERTIFICATE-----
|
|
38
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
39
|
+
dependencies:
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: ast-merge
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - '='
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 7.0.0
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - '='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 7.0.0
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: tree_haver
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - '='
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 7.0.0
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - '='
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 7.0.0
|
|
68
|
+
description: Tree-sitter-backed Ruby family substrate for Structured Merge.
|
|
69
|
+
email:
|
|
70
|
+
- info@structuredmerge.org
|
|
71
|
+
executables: []
|
|
72
|
+
extensions: []
|
|
73
|
+
extra_rdoc_files: []
|
|
74
|
+
files:
|
|
75
|
+
- lib/ruby-merge.rb
|
|
76
|
+
- lib/ruby/merge.rb
|
|
77
|
+
- lib/ruby/merge/version.rb
|
|
78
|
+
homepage: https://github.com/structuredmerge/structuredmerge-ruby
|
|
79
|
+
licenses:
|
|
80
|
+
- AGPL-3.0-only
|
|
81
|
+
- PolyForm-Small-Business-1.0.0
|
|
82
|
+
metadata:
|
|
83
|
+
homepage_uri: https://structuredmerge.org
|
|
84
|
+
source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.0.0
|
|
85
|
+
changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.0.0/CHANGELOG.md
|
|
86
|
+
bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
|
|
87
|
+
documentation_uri: https://www.rubydoc.info/gems/ruby-merge/7.0.0
|
|
88
|
+
funding_uri: https://github.com/sponsors/pboling
|
|
89
|
+
wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
|
|
90
|
+
discord_uri: https://discord.gg/3qme4XHNKN
|
|
91
|
+
rubygems_mfa_required: 'true'
|
|
92
|
+
rdoc_options: []
|
|
93
|
+
require_paths:
|
|
94
|
+
- lib
|
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: 4.0.0
|
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '0'
|
|
105
|
+
requirements: []
|
|
106
|
+
rubygems_version: 4.0.10
|
|
107
|
+
specification_version: 4
|
|
108
|
+
summary: Structured Merge Ruby substrate analysis for Ruby
|
|
109
|
+
test_files: []
|
metadata.gz.sig
ADDED
|
Binary file
|