markdown-merge 7.0.0 → 7.1.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
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.md +13 -0
- data/README.md +681 -0
- data/lib/markdown/merge/backend_support.rb +200 -0
- data/lib/markdown/merge/cleanse/block_spacing.rb +248 -0
- data/lib/markdown/merge/cleanse/code_fence_spacing.rb +294 -0
- data/lib/markdown/merge/cleanse/condensed_link_refs.rb +411 -0
- data/lib/markdown/merge/cleanse/list_marker_duplication.rb +66 -0
- data/lib/markdown/merge/cleanse/templating_corruption.rb +86 -0
- data/lib/markdown/merge/cleanse.rb +44 -0
- data/lib/markdown/merge/code_block_match_refiner.rb +111 -0
- data/lib/markdown/merge/code_block_merger.rb +742 -0
- data/lib/markdown/merge/comment_tracker.rb +42 -0
- data/lib/markdown/merge/conflict_resolver.rb +199 -0
- data/lib/markdown/merge/debug_logger.rb +26 -0
- data/lib/markdown/merge/document_problems.rb +190 -0
- data/lib/markdown/merge/file_aligner.rb +496 -0
- data/lib/markdown/merge/file_analysis.rb +689 -0
- data/lib/markdown/merge/file_analysis_base.rb +766 -0
- data/lib/markdown/merge/freeze_node.rb +93 -0
- data/lib/markdown/merge/gap_line_node.rb +142 -0
- data/lib/markdown/merge/link_definition_formatter.rb +49 -0
- data/lib/markdown/merge/link_definition_node.rb +157 -0
- data/lib/markdown/merge/link_parser.rb +421 -0
- data/lib/markdown/merge/link_reference_rehydrator.rb +320 -0
- data/lib/markdown/merge/list_match_refiner.rb +98 -0
- data/lib/markdown/merge/list_merger.rb +322 -0
- data/lib/markdown/merge/markdown_structure.rb +123 -0
- data/lib/markdown/merge/merge_result.rb +483 -0
- data/lib/markdown/merge/node_type_normalizer.rb +126 -0
- data/lib/markdown/merge/output_builder.rb +248 -0
- data/lib/markdown/merge/partial_template_merger.rb +555 -0
- data/lib/markdown/merge/preservation_support.rb +291 -0
- data/lib/markdown/merge/smart_merger.rb +269 -0
- data/lib/markdown/merge/smart_merger_base.rb +1490 -0
- data/lib/markdown/merge/table_match_algorithm.rb +499 -0
- data/lib/markdown/merge/table_match_refiner.rb +132 -0
- data/lib/markdown/merge/version.rb +5 -3
- data/lib/markdown/merge/whitespace_normalizer.rb +243 -0
- data/lib/markdown/merge/wrapper_support.rb +194 -0
- data/lib/markdown/merge.rb +233 -87
- data/lib/markdown-merge.rb +7 -1
- data/sig/markdown/merge.rbs +8 -0
- data.tar.gz.sig +0 -0
- metadata +287 -15
- metadata.gz.sig +0 -0
data/lib/markdown/merge.rb
CHANGED
|
@@ -1,29 +1,98 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require 'ast/merge'
|
|
4
|
+
require 'tree_haver'
|
|
5
|
+
require_relative 'merge/version'
|
|
4
6
|
|
|
5
7
|
module Markdown
|
|
6
8
|
module Merge
|
|
7
|
-
PACKAGE_NAME =
|
|
9
|
+
PACKAGE_NAME = 'markdown-merge'
|
|
8
10
|
BACKEND_REFERENCES = {
|
|
9
|
-
|
|
11
|
+
'kreuzberg-language-pack' => TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND,
|
|
12
|
+
'commonmarker' => TreeHaver::BackendReference.new(id: 'commonmarker', family: 'native').freeze,
|
|
13
|
+
'markly' => TreeHaver::BackendReference.new(id: 'markly', family: 'native').freeze,
|
|
14
|
+
'kramdown' => TreeHaver::BackendReference.new(id: 'kramdown', family: 'native').freeze
|
|
10
15
|
}.freeze
|
|
16
|
+
BACKEND_REGISTRY = Struct.new(:registered, :mutex).new(false, Mutex.new)
|
|
17
|
+
|
|
18
|
+
class Error < Ast::Merge::Error; end
|
|
19
|
+
|
|
20
|
+
class ParseError < Ast::Merge::ParseError
|
|
21
|
+
def initialize(message = nil, content: nil, errors: [])
|
|
22
|
+
super(message, errors: errors, content: content)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class TemplateParseError < ParseError; end
|
|
27
|
+
|
|
28
|
+
class DestinationParseError < ParseError; end
|
|
29
|
+
|
|
30
|
+
class CorruptionDetectedError < Error; end
|
|
31
|
+
|
|
32
|
+
autoload :BackendSupport, 'markdown/merge/backend_support'
|
|
33
|
+
autoload :Cleanse, 'markdown/merge/cleanse'
|
|
34
|
+
autoload :CodeBlockMatchRefiner, 'markdown/merge/code_block_match_refiner'
|
|
35
|
+
autoload :CodeBlockMerger, 'markdown/merge/code_block_merger'
|
|
36
|
+
autoload :CommentTracker, 'markdown/merge/comment_tracker'
|
|
37
|
+
autoload :ConflictResolver, 'markdown/merge/conflict_resolver'
|
|
38
|
+
autoload :DebugLogger, 'markdown/merge/debug_logger'
|
|
39
|
+
autoload :DocumentProblems, 'markdown/merge/document_problems'
|
|
40
|
+
autoload :FileAligner, 'markdown/merge/file_aligner'
|
|
41
|
+
autoload :FileAnalysis, 'markdown/merge/file_analysis'
|
|
42
|
+
autoload :FileAnalysisBase, 'markdown/merge/file_analysis_base'
|
|
43
|
+
autoload :FreezeNode, 'markdown/merge/freeze_node'
|
|
44
|
+
autoload :GapLineNode, 'markdown/merge/gap_line_node'
|
|
45
|
+
autoload :LinkDefinitionFormatter, 'markdown/merge/link_definition_formatter'
|
|
46
|
+
autoload :LinkDefinitionNode, 'markdown/merge/link_definition_node'
|
|
47
|
+
autoload :LinkParser, 'markdown/merge/link_parser'
|
|
48
|
+
autoload :LinkReferenceRehydrator, 'markdown/merge/link_reference_rehydrator'
|
|
49
|
+
autoload :ListMatchRefiner, 'markdown/merge/list_match_refiner'
|
|
50
|
+
autoload :ListMerger, 'markdown/merge/list_merger'
|
|
51
|
+
autoload :MarkdownStructure, 'markdown/merge/markdown_structure'
|
|
52
|
+
autoload :MergeResult, 'markdown/merge/merge_result'
|
|
53
|
+
autoload :NodeTypeNormalizer, 'markdown/merge/node_type_normalizer'
|
|
54
|
+
autoload :OutputBuilder, 'markdown/merge/output_builder'
|
|
55
|
+
autoload :PartialTemplateMerger, 'markdown/merge/partial_template_merger'
|
|
56
|
+
autoload :PreservationSupport, 'markdown/merge/preservation_support'
|
|
57
|
+
autoload :SmartMerger, 'markdown/merge/smart_merger'
|
|
58
|
+
autoload :SmartMergerBase, 'markdown/merge/smart_merger_base'
|
|
59
|
+
autoload :TableMatchAlgorithm, 'markdown/merge/table_match_algorithm'
|
|
60
|
+
autoload :TableMatchRefiner, 'markdown/merge/table_match_refiner'
|
|
61
|
+
autoload :WhitespaceNormalizer, 'markdown/merge/whitespace_normalizer'
|
|
62
|
+
autoload :WrapperSupport, 'markdown/merge/wrapper_support'
|
|
63
|
+
|
|
64
|
+
def register_backend!
|
|
65
|
+
BACKEND_REGISTRY.mutex.synchronize do
|
|
66
|
+
return if BACKEND_REGISTRY.registered
|
|
67
|
+
|
|
68
|
+
TreeHaver::BackendRegistry.register(BACKEND_REFERENCES.fetch('kreuzberg-language-pack'))
|
|
69
|
+
|
|
70
|
+
grammar_finder = TreeHaver::GrammarFinder.new(:markdown)
|
|
71
|
+
grammar_finder.register! if grammar_finder.available?
|
|
72
|
+
|
|
73
|
+
BACKEND_REGISTRY.registered = true
|
|
74
|
+
end
|
|
75
|
+
end
|
|
11
76
|
|
|
12
77
|
def markdown_feature_profile
|
|
13
78
|
{
|
|
14
|
-
family:
|
|
15
|
-
supported_dialects: [
|
|
79
|
+
family: 'markdown',
|
|
80
|
+
supported_dialects: ['markdown'],
|
|
16
81
|
supported_policies: []
|
|
17
82
|
}
|
|
18
83
|
end
|
|
19
84
|
|
|
20
85
|
def available_markdown_backends
|
|
21
|
-
BACKEND_REFERENCES.
|
|
86
|
+
BACKEND_REFERENCES.filter_map do |backend_id, reference|
|
|
87
|
+
reference if markdown_backend_available_for_analysis?(backend_id)
|
|
88
|
+
end
|
|
22
89
|
end
|
|
23
90
|
|
|
24
91
|
def markdown_backend_feature_profile(backend: nil)
|
|
25
92
|
resolved_backend = resolve_backend(backend)
|
|
26
|
-
|
|
93
|
+
unless BACKEND_REFERENCES.key?(resolved_backend)
|
|
94
|
+
return unsupported_feature_result("Unsupported Markdown backend #{resolved_backend}.")
|
|
95
|
+
end
|
|
27
96
|
|
|
28
97
|
markdown_feature_profile.merge(
|
|
29
98
|
backend: resolved_backend,
|
|
@@ -46,37 +115,33 @@ module Markdown
|
|
|
46
115
|
end
|
|
47
116
|
|
|
48
117
|
def parse_markdown(source, dialect, backend: nil)
|
|
49
|
-
return unsupported_feature_result("Unsupported Markdown dialect #{dialect}.") unless dialect ==
|
|
118
|
+
return unsupported_feature_result("Unsupported Markdown dialect #{dialect}.") unless dialect == 'markdown'
|
|
50
119
|
|
|
51
120
|
resolved_backend = resolve_backend(backend)
|
|
52
|
-
unless resolved_backend
|
|
121
|
+
unless BACKEND_REFERENCES.key?(resolved_backend)
|
|
53
122
|
return unsupported_feature_result("Unsupported Markdown backend #{resolved_backend}.")
|
|
54
123
|
end
|
|
55
124
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
59
|
-
|
|
125
|
+
register_backend!
|
|
126
|
+
parser = TreeHaver.with_backend(resolved_backend) { TreeHaver.parser_for(:markdown) }
|
|
127
|
+
tree = parser.parse(source)
|
|
128
|
+
collect_parse_errors(tree.root_node)
|
|
60
129
|
|
|
61
130
|
normalized_source = normalize_source(source)
|
|
62
131
|
{
|
|
63
132
|
ok: true,
|
|
64
133
|
diagnostics: [],
|
|
65
134
|
analysis: {
|
|
66
|
-
kind:
|
|
135
|
+
kind: 'markdown',
|
|
67
136
|
dialect: dialect,
|
|
68
137
|
normalized_source: normalized_source,
|
|
69
|
-
root_kind:
|
|
138
|
+
root_kind: 'document',
|
|
70
139
|
owners: collect_markdown_owners(normalized_source)
|
|
71
140
|
},
|
|
72
141
|
policies: []
|
|
73
142
|
}
|
|
74
|
-
rescue StandardError => e
|
|
75
|
-
|
|
76
|
-
ok: false,
|
|
77
|
-
diagnostics: [{ severity: "error", category: "parse_error", message: e.message }],
|
|
78
|
-
policies: []
|
|
79
|
-
}
|
|
143
|
+
rescue TreeHaver::Error, StandardError => e
|
|
144
|
+
parse_failure_result(e)
|
|
80
145
|
end
|
|
81
146
|
|
|
82
147
|
def match_markdown_owners(template, destination)
|
|
@@ -85,8 +150,8 @@ module Markdown
|
|
|
85
150
|
|
|
86
151
|
{
|
|
87
152
|
matched: template[:owners]
|
|
88
|
-
|
|
89
|
-
|
|
153
|
+
.filter { |owner| destination_paths[owner[:path]] }
|
|
154
|
+
.map { |owner| { template_path: owner[:path], destination_path: owner[:path] } },
|
|
90
155
|
unmatched_template: template[:owners].map { |owner| owner[:path] }.reject { |path| destination_paths[path] },
|
|
91
156
|
unmatched_destination: destination[:owners].map { |owner| owner[:path] }.reject { |path| template_paths[path] }
|
|
92
157
|
}
|
|
@@ -109,9 +174,9 @@ module Markdown
|
|
|
109
174
|
)
|
|
110
175
|
destination_paths = destination_sections.to_h { |section| [section[:path], true] }
|
|
111
176
|
merged_sections = destination_sections.map { |section| section[:text] }.reject(&:empty?) +
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
177
|
+
template_sections
|
|
178
|
+
.reject { |section| destination_paths[section[:path]] || section[:text].empty? }
|
|
179
|
+
.map { |section| section[:text] }
|
|
115
180
|
|
|
116
181
|
{
|
|
117
182
|
ok: true,
|
|
@@ -123,7 +188,7 @@ module Markdown
|
|
|
123
188
|
|
|
124
189
|
def markdown_embedded_families(analysis)
|
|
125
190
|
analysis[:owners].filter_map do |owner|
|
|
126
|
-
next unless owner[:owner_kind] ==
|
|
191
|
+
next unless owner[:owner_kind] == 'code_fence'
|
|
127
192
|
next if owner[:info_string].to_s.empty?
|
|
128
193
|
|
|
129
194
|
family = code_fence_family(owner[:info_string])
|
|
@@ -142,13 +207,13 @@ module Markdown
|
|
|
142
207
|
def markdown_discovered_surfaces(analysis)
|
|
143
208
|
markdown_embedded_families(analysis).map do |candidate|
|
|
144
209
|
Ast::Merge.discovered_surface(
|
|
145
|
-
surface_kind:
|
|
210
|
+
surface_kind: 'markdown_fenced_code_block',
|
|
146
211
|
declared_language: candidate[:language],
|
|
147
212
|
effective_language: candidate[:dialect],
|
|
148
213
|
address: "document[0] > fenced_code_block[#{candidate[:path]}]",
|
|
149
|
-
parent_address:
|
|
150
|
-
owner: Ast::Merge.surface_owner_ref(kind:
|
|
151
|
-
reconstruction_strategy:
|
|
214
|
+
parent_address: 'document[0]',
|
|
215
|
+
owner: Ast::Merge.surface_owner_ref(kind: 'structural_owner', address: candidate[:path]),
|
|
216
|
+
reconstruction_strategy: 'portable_write',
|
|
152
217
|
metadata: {
|
|
153
218
|
family: candidate[:family],
|
|
154
219
|
dialect: candidate[:dialect],
|
|
@@ -158,13 +223,13 @@ module Markdown
|
|
|
158
223
|
end
|
|
159
224
|
end
|
|
160
225
|
|
|
161
|
-
def markdown_delegated_child_operations(analysis, parent_operation_id:
|
|
226
|
+
def markdown_delegated_child_operations(analysis, parent_operation_id: 'markdown-document-0')
|
|
162
227
|
markdown_discovered_surfaces(analysis).each_with_index.map do |surface, index|
|
|
163
228
|
Ast::Merge.delegated_child_operation(
|
|
164
229
|
operation_id: "markdown-fence-#{index}",
|
|
165
230
|
parent_operation_id: parent_operation_id,
|
|
166
|
-
requested_strategy:
|
|
167
|
-
language_chain: [
|
|
231
|
+
requested_strategy: 'delegate_child_surface',
|
|
232
|
+
language_chain: ['markdown', surface[:effective_language]],
|
|
168
233
|
surface: surface
|
|
169
234
|
)
|
|
170
235
|
end
|
|
@@ -183,24 +248,27 @@ module Markdown
|
|
|
183
248
|
|
|
184
249
|
owner_path = operation.dig(:surface, :owner, :address)
|
|
185
250
|
range = ranges[owner_path]
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
251
|
+
if range.nil?
|
|
252
|
+
return {
|
|
253
|
+
ok: false,
|
|
254
|
+
diagnostics: [{ severity: 'error', category: 'configuration_error',
|
|
255
|
+
message: "missing fenced-code range for #{owner_path}" }],
|
|
256
|
+
policies: []
|
|
257
|
+
}
|
|
258
|
+
end
|
|
191
259
|
|
|
192
260
|
{ range: range, output: output }
|
|
193
261
|
end
|
|
194
262
|
|
|
195
263
|
replacements.sort_by { |entry| -entry[:range][:start] }.each do |entry|
|
|
196
|
-
body_lines = entry[:output].empty? ? [] : entry[:output].sub(/\n\z/,
|
|
264
|
+
body_lines = entry[:output].empty? ? [] : entry[:output].sub(/\n\z/, '').split("\n")
|
|
197
265
|
lines[entry[:range][:start] + 1...entry[:range][:end]] = body_lines
|
|
198
266
|
end
|
|
199
267
|
|
|
200
268
|
{
|
|
201
269
|
ok: true,
|
|
202
270
|
diagnostics: [],
|
|
203
|
-
output: "#{lines.join("\n").sub(/\n+\z/,
|
|
271
|
+
output: "#{lines.join("\n").sub(/\n+\z/, '')}\n",
|
|
204
272
|
policies: []
|
|
205
273
|
}
|
|
206
274
|
end
|
|
@@ -208,8 +276,8 @@ module Markdown
|
|
|
208
276
|
def merge_markdown_with_nested_outputs(template_source, destination_source, dialect, nested_outputs, backend: nil)
|
|
209
277
|
Ast::Merge.execute_nested_merge(
|
|
210
278
|
nested_outputs,
|
|
211
|
-
default_family:
|
|
212
|
-
request_id_prefix:
|
|
279
|
+
default_family: 'markdown',
|
|
280
|
+
request_id_prefix: 'nested_markdown_child',
|
|
213
281
|
merge_parent: -> { merge_markdown(template_source, destination_source, dialect, backend: backend) },
|
|
214
282
|
discover_operations: lambda { |merged_output|
|
|
215
283
|
analysis = parse_markdown(merged_output, dialect, backend: backend)
|
|
@@ -232,10 +300,11 @@ module Markdown
|
|
|
232
300
|
)
|
|
233
301
|
end
|
|
234
302
|
|
|
235
|
-
def merge_markdown_with_reviewed_nested_outputs(template_source, destination_source, dialect, review_state,
|
|
303
|
+
def merge_markdown_with_reviewed_nested_outputs(template_source, destination_source, dialect, review_state,
|
|
304
|
+
applied_children, backend: nil)
|
|
236
305
|
Ast::Merge.execute_reviewed_nested_merge(
|
|
237
306
|
review_state,
|
|
238
|
-
|
|
307
|
+
'markdown',
|
|
239
308
|
applied_children,
|
|
240
309
|
merge_parent: -> { merge_markdown(template_source, destination_source, dialect, backend: backend) },
|
|
241
310
|
discover_operations: lambda { |merged_output|
|
|
@@ -259,9 +328,13 @@ module Markdown
|
|
|
259
328
|
)
|
|
260
329
|
end
|
|
261
330
|
|
|
262
|
-
def merge_markdown_with_reviewed_nested_outputs_from_replay_bundle(template_source, destination_source, dialect,
|
|
263
|
-
|
|
264
|
-
|
|
331
|
+
def merge_markdown_with_reviewed_nested_outputs_from_replay_bundle(template_source, destination_source, dialect,
|
|
332
|
+
replay_bundle, backend: nil)
|
|
333
|
+
execution = Array(replay_bundle[:reviewed_nested_executions]).find { |entry| entry[:family] == 'markdown' }
|
|
334
|
+
unless execution
|
|
335
|
+
return { ok: false,
|
|
336
|
+
diagnostics: [{ severity: 'error', category: 'configuration_error', message: 'review replay bundle does not include a reviewed nested execution for markdown.' }], policies: [] }
|
|
337
|
+
end
|
|
265
338
|
|
|
266
339
|
merge_markdown_with_reviewed_nested_outputs(
|
|
267
340
|
template_source,
|
|
@@ -273,9 +346,13 @@ module Markdown
|
|
|
273
346
|
)
|
|
274
347
|
end
|
|
275
348
|
|
|
276
|
-
def merge_markdown_with_reviewed_nested_outputs_from_review_state(template_source, destination_source, dialect,
|
|
277
|
-
|
|
278
|
-
|
|
349
|
+
def merge_markdown_with_reviewed_nested_outputs_from_review_state(template_source, destination_source, dialect,
|
|
350
|
+
review_state, backend: nil)
|
|
351
|
+
execution = Array(review_state[:reviewed_nested_executions]).find { |entry| entry[:family] == 'markdown' }
|
|
352
|
+
unless execution
|
|
353
|
+
return { ok: false,
|
|
354
|
+
diagnostics: [{ severity: 'error', category: 'configuration_error', message: 'review state does not include a reviewed nested execution for markdown.' }], policies: [] }
|
|
355
|
+
end
|
|
279
356
|
|
|
280
357
|
merge_markdown_with_reviewed_nested_outputs(
|
|
281
358
|
template_source,
|
|
@@ -287,9 +364,13 @@ module Markdown
|
|
|
287
364
|
)
|
|
288
365
|
end
|
|
289
366
|
|
|
290
|
-
def merge_markdown_with_reviewed_nested_outputs_from_replay_bundle_envelope(template_source, destination_source,
|
|
367
|
+
def merge_markdown_with_reviewed_nested_outputs_from_replay_bundle_envelope(template_source, destination_source,
|
|
368
|
+
dialect, envelope, backend: nil)
|
|
291
369
|
replay_bundle, import_error = Ast::Merge.import_review_replay_bundle_envelope(envelope)
|
|
292
|
-
|
|
370
|
+
if import_error
|
|
371
|
+
return { ok: false,
|
|
372
|
+
diagnostics: [{ severity: 'error', category: import_error[:category], message: import_error[:message] }], policies: [] }
|
|
373
|
+
end
|
|
293
374
|
|
|
294
375
|
merge_markdown_with_reviewed_nested_outputs_from_replay_bundle(
|
|
295
376
|
template_source,
|
|
@@ -300,9 +381,13 @@ module Markdown
|
|
|
300
381
|
)
|
|
301
382
|
end
|
|
302
383
|
|
|
303
|
-
def merge_markdown_with_reviewed_nested_outputs_from_review_state_envelope(template_source, destination_source,
|
|
384
|
+
def merge_markdown_with_reviewed_nested_outputs_from_review_state_envelope(template_source, destination_source,
|
|
385
|
+
dialect, envelope, backend: nil)
|
|
304
386
|
review_state, import_error = Ast::Merge.import_conformance_manifest_review_state_envelope(envelope)
|
|
305
|
-
|
|
387
|
+
if import_error
|
|
388
|
+
return { ok: false,
|
|
389
|
+
diagnostics: [{ severity: 'error', category: import_error[:category], message: import_error[:message] }], policies: [] }
|
|
390
|
+
end
|
|
306
391
|
|
|
307
392
|
merge_markdown_with_reviewed_nested_outputs_from_review_state(
|
|
308
393
|
template_source,
|
|
@@ -319,12 +404,12 @@ module Markdown
|
|
|
319
404
|
|
|
320
405
|
def slugify(value)
|
|
321
406
|
slug = value
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
slug.empty? ?
|
|
407
|
+
.strip
|
|
408
|
+
.downcase
|
|
409
|
+
.gsub(/[`*_~\[\]()<>]/, '')
|
|
410
|
+
.gsub(/[^a-z0-9]+/, '-')
|
|
411
|
+
.gsub(/\A-+|-+\z/, '')
|
|
412
|
+
slug.empty? ? 'section' : slug
|
|
328
413
|
end
|
|
329
414
|
|
|
330
415
|
def collect_markdown_owners(source)
|
|
@@ -340,7 +425,7 @@ module Markdown
|
|
|
340
425
|
level = heading[1].length
|
|
341
426
|
owners << {
|
|
342
427
|
path: "/heading/#{heading_index}",
|
|
343
|
-
owner_kind:
|
|
428
|
+
owner_kind: 'heading',
|
|
344
429
|
match_key: "h#{level}:#{slugify(heading[2])}",
|
|
345
430
|
level: level
|
|
346
431
|
}
|
|
@@ -356,8 +441,8 @@ module Markdown
|
|
|
356
441
|
info_string = fence[2].strip.split(/\s+/).first.to_s
|
|
357
442
|
owners << {
|
|
358
443
|
path: "/code_fence/#{code_fence_index}",
|
|
359
|
-
owner_kind:
|
|
360
|
-
match_key: "fence:#{info_string.empty? ?
|
|
444
|
+
owner_kind: 'code_fence',
|
|
445
|
+
match_key: "fence:#{info_string.empty? ? 'plain' : info_string}",
|
|
361
446
|
**(info_string.empty? ? {} : { info_string: info_string })
|
|
362
447
|
}
|
|
363
448
|
code_fence_index += 1
|
|
@@ -366,8 +451,8 @@ module Markdown
|
|
|
366
451
|
while index < lines.length
|
|
367
452
|
trimmed = lines[index].strip
|
|
368
453
|
break if trimmed.length >= marker_length &&
|
|
369
|
-
|
|
370
|
-
|
|
454
|
+
trimmed.start_with?(marker_char * marker_length) &&
|
|
455
|
+
trimmed.delete(marker_char).empty?
|
|
371
456
|
|
|
372
457
|
index += 1
|
|
373
458
|
end
|
|
@@ -407,8 +492,8 @@ module Markdown
|
|
|
407
492
|
while index < lines.length
|
|
408
493
|
trimmed = lines[index].strip
|
|
409
494
|
break if trimmed.length >= marker_length &&
|
|
410
|
-
|
|
411
|
-
|
|
495
|
+
trimmed.start_with?(marker_char * marker_length) &&
|
|
496
|
+
trimmed.delete(marker_char).empty?
|
|
412
497
|
|
|
413
498
|
index += 1
|
|
414
499
|
end
|
|
@@ -458,8 +543,8 @@ module Markdown
|
|
|
458
543
|
while cursor < lines.length
|
|
459
544
|
trimmed = lines[cursor].strip
|
|
460
545
|
if trimmed.length >= marker_length &&
|
|
461
|
-
|
|
462
|
-
|
|
546
|
+
trimmed.start_with?(marker_char * marker_length) &&
|
|
547
|
+
trimmed.delete(marker_char).empty?
|
|
463
548
|
closing_index = cursor
|
|
464
549
|
break
|
|
465
550
|
end
|
|
@@ -481,43 +566,81 @@ module Markdown
|
|
|
481
566
|
|
|
482
567
|
def code_fence_family(info_string)
|
|
483
568
|
case info_string.to_s.downcase
|
|
484
|
-
when
|
|
485
|
-
|
|
486
|
-
when
|
|
487
|
-
|
|
488
|
-
when
|
|
489
|
-
|
|
490
|
-
when
|
|
491
|
-
|
|
492
|
-
when
|
|
493
|
-
|
|
494
|
-
when
|
|
495
|
-
|
|
569
|
+
when 'ts', 'typescript'
|
|
570
|
+
'typescript'
|
|
571
|
+
when 'rust', 'rs'
|
|
572
|
+
'rust'
|
|
573
|
+
when 'go'
|
|
574
|
+
'go'
|
|
575
|
+
when 'json', 'jsonc', 'json5'
|
|
576
|
+
'json'
|
|
577
|
+
when 'yaml', 'yml'
|
|
578
|
+
'yaml'
|
|
579
|
+
when 'toml'
|
|
580
|
+
'toml'
|
|
496
581
|
end
|
|
497
582
|
end
|
|
498
583
|
|
|
499
584
|
def code_fence_dialect(info_string, family)
|
|
500
585
|
case family
|
|
501
|
-
when
|
|
586
|
+
when 'typescript', 'rust', 'go', 'yaml', 'toml'
|
|
502
587
|
family
|
|
503
|
-
when
|
|
504
|
-
info_string.to_s.downcase
|
|
588
|
+
when 'json'
|
|
589
|
+
%w[json jsonc json5].include?(info_string.to_s.downcase) ? info_string.to_s.downcase : 'json'
|
|
505
590
|
end
|
|
506
591
|
end
|
|
507
592
|
|
|
508
593
|
def resolve_backend(backend)
|
|
509
|
-
backend.to_s
|
|
594
|
+
return backend.to_s unless backend.to_s.empty?
|
|
595
|
+
|
|
596
|
+
current = TreeHaver.current_backend_id
|
|
597
|
+
return current if BACKEND_REFERENCES.key?(current.to_s) && markdown_backend_available_for_analysis?(current)
|
|
598
|
+
|
|
599
|
+
BACKEND_REFERENCES.keys.find { |backend_id| markdown_backend_available_for_analysis?(backend_id) } ||
|
|
600
|
+
'kreuzberg-language-pack'
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def markdown_backend_available_for_analysis?(backend_id)
|
|
604
|
+
register_backend!
|
|
605
|
+
registrations = TreeHaver.registered_languages(:markdown)
|
|
606
|
+
case backend_id.to_s
|
|
607
|
+
when 'commonmarker', 'markly', 'kramdown'
|
|
608
|
+
registrations.dig(backend_id.to_sym, :backend_module)&.then do |backend_module|
|
|
609
|
+
!backend_module.respond_to?(:available?) || backend_module.available?
|
|
610
|
+
end
|
|
611
|
+
when 'kreuzberg-language-pack'
|
|
612
|
+
registrations.key?(:tree_sitter) || registrations.key?(:tslp)
|
|
613
|
+
else
|
|
614
|
+
false
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
def collect_parse_errors(node)
|
|
619
|
+
raise TreeHaver::NotAvailable, 'Markdown parse returned no root node' unless node
|
|
620
|
+
return unless node.respond_to?(:has_error?) && node.has_error?
|
|
621
|
+
|
|
622
|
+
raise TreeHaver::NotAvailable,
|
|
623
|
+
'Markdown parse contains syntax errors'
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def parse_failure_result(error)
|
|
627
|
+
{
|
|
628
|
+
ok: false,
|
|
629
|
+
diagnostics: [{ severity: 'error', category: 'parse_error', message: error.message }],
|
|
630
|
+
policies: []
|
|
631
|
+
}
|
|
510
632
|
end
|
|
511
633
|
|
|
512
634
|
def unsupported_feature_result(message)
|
|
513
635
|
{
|
|
514
636
|
ok: false,
|
|
515
|
-
diagnostics: [{ severity:
|
|
637
|
+
diagnostics: [{ severity: 'error', category: 'unsupported_feature', message: message }],
|
|
516
638
|
policies: []
|
|
517
639
|
}
|
|
518
640
|
end
|
|
519
641
|
|
|
520
642
|
module_function(
|
|
643
|
+
:register_backend!,
|
|
521
644
|
:markdown_feature_profile,
|
|
522
645
|
:available_markdown_backends,
|
|
523
646
|
:markdown_backend_feature_profile,
|
|
@@ -544,7 +667,30 @@ module Markdown
|
|
|
544
667
|
:code_fence_family,
|
|
545
668
|
:code_fence_dialect,
|
|
546
669
|
:resolve_backend,
|
|
670
|
+
:markdown_backend_available_for_analysis?,
|
|
671
|
+
:collect_parse_errors,
|
|
672
|
+
:parse_failure_result,
|
|
547
673
|
:unsupported_feature_result
|
|
548
674
|
)
|
|
549
675
|
end
|
|
550
676
|
end
|
|
677
|
+
|
|
678
|
+
%w[
|
|
679
|
+
commonmarker/merge/backend
|
|
680
|
+
markly/merge/backend
|
|
681
|
+
].each do |feature|
|
|
682
|
+
require feature
|
|
683
|
+
rescue LoadError
|
|
684
|
+
nil
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
if defined?(Ast::Merge::RSpec::MergeGemRegistry)
|
|
688
|
+
Ast::Merge::RSpec::MergeGemRegistry.register(
|
|
689
|
+
:markdown_merge,
|
|
690
|
+
require_path: 'markdown/merge',
|
|
691
|
+
merger_class: 'Markdown::Merge::SmartMerger',
|
|
692
|
+
test_source: "# Test\n\nParagraph",
|
|
693
|
+
category: :markdown,
|
|
694
|
+
skip_instantiation: true
|
|
695
|
+
)
|
|
696
|
+
end
|
data/lib/markdown-merge.rb
CHANGED
data.tar.gz.sig
CHANGED
|
Binary file
|