markdown-merge 1.0.3 → 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 +99 -482
- data/lib/markdown/merge/backend_support.rb +200 -0
- data/lib/markdown/merge/cleanse/block_spacing.rb +18 -23
- data/lib/markdown/merge/cleanse/code_fence_spacing.rb +16 -16
- data/lib/markdown/merge/cleanse/condensed_link_refs.rb +36 -30
- 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 +5 -3
- data/lib/markdown/merge/code_block_match_refiner.rb +111 -0
- data/lib/markdown/merge/code_block_merger.rb +489 -47
- data/lib/markdown/merge/comment_tracker.rb +42 -0
- data/lib/markdown/merge/conflict_resolver.rb +77 -6
- data/lib/markdown/merge/debug_logger.rb +2 -2
- data/lib/markdown/merge/document_problems.rb +3 -3
- data/lib/markdown/merge/file_aligner.rb +433 -133
- data/lib/markdown/merge/file_analysis.rb +387 -51
- data/lib/markdown/merge/file_analysis_base.rb +188 -51
- data/lib/markdown/merge/gap_line_node.rb +14 -8
- data/lib/markdown/merge/link_definition_node.rb +5 -5
- data/lib/markdown/merge/link_parser.rb +60 -60
- data/lib/markdown/merge/link_reference_rehydrator.rb +14 -14
- 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 +3 -3
- data/lib/markdown/merge/merge_result.rb +321 -4
- data/lib/markdown/merge/node_type_normalizer.rb +6 -6
- data/lib/markdown/merge/output_builder.rb +101 -19
- data/lib/markdown/merge/partial_template_merger.rb +248 -27
- data/lib/markdown/merge/preservation_support.rb +291 -0
- data/lib/markdown/merge/smart_merger.rb +62 -14
- data/lib/markdown/merge/smart_merger_base.rb +929 -60
- data/lib/markdown/merge/table_match_algorithm.rb +22 -27
- data/lib/markdown/merge/table_match_refiner.rb +6 -10
- data/lib/markdown/merge/version.rb +5 -4
- data/lib/markdown/merge/whitespace_normalizer.rb +25 -33
- data/lib/markdown/merge/wrapper_support.rb +194 -0
- data/lib/markdown/merge.rb +669 -122
- data/lib/markdown-merge.rb +9 -4
- data/sig/markdown/merge.rbs +3 -336
- data.tar.gz.sig +0 -0
- metadata +104 -93
- metadata.gz.sig +0 -0
- data/CHANGELOG.md +0 -308
- data/CITATION.cff +0 -20
- data/CODE_OF_CONDUCT.md +0 -134
- data/CONTRIBUTING.md +0 -227
- data/FUNDING.md +0 -74
- data/LICENSE.txt +0 -21
- data/REEK +0 -0
- data/RUBOCOP.md +0 -71
- data/SECURITY.md +0 -21
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require 'digest'
|
|
4
4
|
|
|
5
5
|
module Markdown
|
|
6
6
|
module Merge
|
|
@@ -41,18 +41,54 @@ module Markdown
|
|
|
41
41
|
class FileAnalysis < FileAnalysisBase
|
|
42
42
|
# Default freeze token for identifying freeze blocks
|
|
43
43
|
# @return [String]
|
|
44
|
-
DEFAULT_FREEZE_TOKEN =
|
|
44
|
+
DEFAULT_FREEZE_TOKEN = 'markdown-merge'
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
class << self
|
|
47
|
+
def default_backend
|
|
48
|
+
:auto
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def default_freeze_token
|
|
52
|
+
self::DEFAULT_FREEZE_TOKEN
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def default_parser_options
|
|
56
|
+
{}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def default_freeze_node_class
|
|
60
|
+
Markdown::Merge::FreezeNode
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @return [Symbol] The backend being used (:commonmarker, :markly, :kramdown)
|
|
47
65
|
attr_reader :backend
|
|
48
66
|
|
|
49
67
|
# @return [Hash] Parser-specific options
|
|
50
68
|
attr_reader :parser_options
|
|
51
69
|
|
|
70
|
+
Location = Struct.new(:start_line, :end_line, keyword_init: true)
|
|
71
|
+
HeadingSectionOwner = Struct.new(:location, :heading_text, :heading_source, :level, :base, keyword_init: true)
|
|
72
|
+
LinkDefinitionOwner = Struct.new(:location, :label, :url, :title, :source, keyword_init: true)
|
|
73
|
+
HtmlCommentOwner = Struct.new(:location, :text, :source, keyword_init: true)
|
|
74
|
+
ListItemOwner = Struct.new(:location, :source, :text, :depth, :marker, keyword_init: true)
|
|
75
|
+
InlineReferenceOwner = Struct.new(
|
|
76
|
+
:location,
|
|
77
|
+
:line,
|
|
78
|
+
:start_column,
|
|
79
|
+
:end_column,
|
|
80
|
+
:source,
|
|
81
|
+
:reference_kind,
|
|
82
|
+
:label,
|
|
83
|
+
:labels,
|
|
84
|
+
keyword_init: true
|
|
85
|
+
)
|
|
86
|
+
TableRowOwner = Struct.new(:location, :source, :text, keyword_init: true)
|
|
87
|
+
|
|
52
88
|
# Initialize file analysis with tree_haver backend.
|
|
53
89
|
#
|
|
54
90
|
# @param source [String] Markdown source code to analyze
|
|
55
|
-
# @param backend [Symbol] Backend to use (:commonmarker, :markly, :auto)
|
|
91
|
+
# @param backend [Symbol] Backend to use (:commonmarker, :markly, :kramdown, :auto)
|
|
56
92
|
# @param freeze_token [String] Token for freeze block markers
|
|
57
93
|
# @param signature_generator [Proc, nil] Custom signature generator
|
|
58
94
|
# @param parser_options [Hash] Backend-specific parser options
|
|
@@ -60,13 +96,13 @@ module Markdown
|
|
|
60
96
|
# For markly: { flags: Markly::DEFAULT, extensions: [:table] }
|
|
61
97
|
def initialize(
|
|
62
98
|
source,
|
|
63
|
-
backend:
|
|
64
|
-
freeze_token:
|
|
99
|
+
backend: self.class.default_backend,
|
|
100
|
+
freeze_token: self.class.default_freeze_token,
|
|
65
101
|
signature_generator: nil,
|
|
66
102
|
**parser_options
|
|
67
103
|
)
|
|
68
104
|
@requested_backend = backend
|
|
69
|
-
@parser_options = parser_options
|
|
105
|
+
@parser_options = self.class.default_parser_options.merge(parser_options)
|
|
70
106
|
|
|
71
107
|
# Resolve and initialize the backend
|
|
72
108
|
@backend = resolve_backend(backend)
|
|
@@ -116,7 +152,7 @@ module Markdown
|
|
|
116
152
|
#
|
|
117
153
|
# @return [Class] Markdown::Merge::FreezeNode
|
|
118
154
|
def freeze_node_class
|
|
119
|
-
|
|
155
|
+
self.class.default_freeze_node_class
|
|
120
156
|
end
|
|
121
157
|
|
|
122
158
|
# Check if value is a tree_haver node.
|
|
@@ -150,18 +186,21 @@ module Markdown
|
|
|
150
186
|
def compute_parser_signature(node)
|
|
151
187
|
# Get canonical type from wrapper or normalize raw type
|
|
152
188
|
canonical_type = if Ast::Merge::NodeTyping.typed_node?(node)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
189
|
+
Ast::Merge::NodeTyping.merge_type_for(node)
|
|
190
|
+
else
|
|
191
|
+
NodeTypeNormalizer.canonical_type(node.type, @backend)
|
|
192
|
+
end
|
|
157
193
|
|
|
158
194
|
# Unwrap to access underlying node methods
|
|
159
195
|
raw_node = Ast::Merge::NodeTyping.unwrap(node)
|
|
160
196
|
|
|
161
197
|
case canonical_type
|
|
162
198
|
when :heading
|
|
163
|
-
|
|
164
|
-
|
|
199
|
+
level = raw_node.header_level
|
|
200
|
+
# H1 is the document title — treat as a singleton (see FileAnalysisBase for rationale)
|
|
201
|
+
return [:heading, 1] if level == 1
|
|
202
|
+
|
|
203
|
+
[:heading, level, extract_text_content(raw_node)]
|
|
165
204
|
when :paragraph
|
|
166
205
|
# Content-based: Match paragraphs by content hash (first 32 chars of digest)
|
|
167
206
|
text = extract_text_content(raw_node)
|
|
@@ -251,8 +290,300 @@ module Markdown
|
|
|
251
290
|
nodes
|
|
252
291
|
end
|
|
253
292
|
|
|
293
|
+
def heading_section_owners
|
|
294
|
+
headings = Array(statements).filter_map do |statement|
|
|
295
|
+
next unless heading_statement?(statement)
|
|
296
|
+
|
|
297
|
+
build_heading_owner(statement)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
headings.each_with_index.map do |owner, index|
|
|
301
|
+
branch_end_line = branch_end_line(headings, index)
|
|
302
|
+
HeadingSectionOwner.new(
|
|
303
|
+
location: Location.new(start_line: owner.location.start_line, end_line: branch_end_line),
|
|
304
|
+
heading_text: owner.heading_text,
|
|
305
|
+
heading_source: owner.heading_source,
|
|
306
|
+
level: owner.level,
|
|
307
|
+
base: owner.base
|
|
308
|
+
)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def link_definition_owners
|
|
313
|
+
Array(statements).filter_map do |statement|
|
|
314
|
+
next unless statement.respond_to?(:merge_type) && statement.merge_type == :link_definition
|
|
315
|
+
|
|
316
|
+
position = statement.source_position
|
|
317
|
+
next unless position
|
|
318
|
+
|
|
319
|
+
LinkDefinitionOwner.new(
|
|
320
|
+
location: Location.new(start_line: position[:start_line], end_line: position[:end_line]),
|
|
321
|
+
label: statement.label,
|
|
322
|
+
url: statement.url,
|
|
323
|
+
title: statement.title,
|
|
324
|
+
source: if statement.respond_to?(:content)
|
|
325
|
+
statement.content
|
|
326
|
+
else
|
|
327
|
+
source_range(position[:start_line], position[:end_line]).chomp
|
|
328
|
+
end
|
|
329
|
+
)
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def html_comment_owners
|
|
334
|
+
comment_tracker.comment_nodes.map do |comment|
|
|
335
|
+
HtmlCommentOwner.new(
|
|
336
|
+
location: Location.new(start_line: comment.location.start_line, end_line: comment.location.end_line),
|
|
337
|
+
text: comment.content,
|
|
338
|
+
source: comment.text
|
|
339
|
+
)
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def list_item_owners
|
|
344
|
+
Array(statements).flat_map do |statement|
|
|
345
|
+
collect_list_item_owners(unwrap_markdown_statement(statement), depth: 0)
|
|
346
|
+
end.sort_by { |owner| [owner.location.start_line, owner.location.end_line] }
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def inline_reference_owners
|
|
350
|
+
source.to_s.lines.each_with_index.flat_map do |line, index|
|
|
351
|
+
inline_references_for_line(line.chomp, index + 1)
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def table_row_owners
|
|
356
|
+
ast_table_lines = {}
|
|
357
|
+
ast_rows = Array(statements).flat_map do |statement|
|
|
358
|
+
node = unwrap_markdown_statement(statement)
|
|
359
|
+
next [] unless node.respond_to?(:type) && node.type.to_s == 'table'
|
|
360
|
+
|
|
361
|
+
table_position = node.source_position
|
|
362
|
+
if table_position
|
|
363
|
+
(table_position[:start_line]..table_position[:end_line]).each do |line|
|
|
364
|
+
ast_table_lines[line] = true
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
Array(node.children).filter_map do |child|
|
|
368
|
+
next unless child.respond_to?(:type) && child.type.to_s == 'table_row'
|
|
369
|
+
|
|
370
|
+
position = child.source_position
|
|
371
|
+
next unless position
|
|
372
|
+
|
|
373
|
+
TableRowOwner.new(
|
|
374
|
+
location: Location.new(start_line: position[:start_line], end_line: position[:end_line]),
|
|
375
|
+
source: source_range(position[:start_line], position[:end_line]),
|
|
376
|
+
text: extract_text_content(child)
|
|
377
|
+
)
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
ast_lines = ast_rows.map { |owner| owner.location.start_line }.to_h { |line| [line, true] }
|
|
381
|
+
loose_rows = source.to_s.lines.each_with_index.filter_map do |line, index|
|
|
382
|
+
line_number = index + 1
|
|
383
|
+
next if ast_lines[line_number]
|
|
384
|
+
next if ast_table_lines[line_number]
|
|
385
|
+
next unless loose_table_row_line?(line)
|
|
386
|
+
|
|
387
|
+
TableRowOwner.new(
|
|
388
|
+
location: Location.new(start_line: line_number, end_line: line_number),
|
|
389
|
+
source: line,
|
|
390
|
+
text: line
|
|
391
|
+
)
|
|
392
|
+
end
|
|
393
|
+
ast_rows + loose_rows
|
|
394
|
+
end
|
|
395
|
+
|
|
254
396
|
private
|
|
255
397
|
|
|
398
|
+
def loose_table_row_line?(line)
|
|
399
|
+
stripped = line.to_s.lstrip
|
|
400
|
+
return false unless stripped.start_with?('|')
|
|
401
|
+
|
|
402
|
+
stripped.include?(' |') || stripped.include?('| ')
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def collect_list_item_owners(node, depth:)
|
|
406
|
+
return [] unless node.respond_to?(:type)
|
|
407
|
+
|
|
408
|
+
canonical_type = NodeTypeNormalizer.canonical_type(node.type, @backend)
|
|
409
|
+
next_depth = %i[list].include?(canonical_type) ? depth + 1 : depth
|
|
410
|
+
owners = []
|
|
411
|
+
if canonical_type == :list_item
|
|
412
|
+
position = node.source_position if node.respond_to?(:source_position)
|
|
413
|
+
if position
|
|
414
|
+
source_text = source_range(position[:start_line], position[:end_line])
|
|
415
|
+
owners << ListItemOwner.new(
|
|
416
|
+
location: Location.new(start_line: position[:start_line], end_line: position[:end_line]),
|
|
417
|
+
source: source_text,
|
|
418
|
+
text: source_text,
|
|
419
|
+
depth: depth,
|
|
420
|
+
marker: list_item_marker(source_text)
|
|
421
|
+
)
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
Array(node.children).each do |child|
|
|
426
|
+
owners.concat(collect_list_item_owners(child, depth: next_depth))
|
|
427
|
+
end
|
|
428
|
+
owners
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def list_item_marker(source_text)
|
|
432
|
+
stripped = source_text.to_s.lines.first.to_s.lstrip
|
|
433
|
+
return stripped[0, 2].strip if stripped.start_with?('- ', '* ')
|
|
434
|
+
|
|
435
|
+
marker = stripped.split(' ', 2).first.to_s
|
|
436
|
+
marker.end_with?('.') ? marker : nil
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def heading_statement?(statement)
|
|
440
|
+
merge_type = if statement.respond_to?(:merge_type)
|
|
441
|
+
statement.merge_type
|
|
442
|
+
else
|
|
443
|
+
unwrap_markdown_statement(statement)&.type
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
%w[heading header].include?(merge_type.to_s)
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def build_heading_owner(statement)
|
|
450
|
+
node = unwrap_markdown_statement(statement)
|
|
451
|
+
position = node&.source_position
|
|
452
|
+
return unless node && position
|
|
453
|
+
|
|
454
|
+
heading_source = source_range(position[:start_line], position[:end_line]).sub(/\n\z/, '')
|
|
455
|
+
heading_text = node.to_plaintext.to_s.sub(/\n+\z/, '')
|
|
456
|
+
HeadingSectionOwner.new(
|
|
457
|
+
location: Location.new(start_line: position[:start_line], end_line: position[:end_line]),
|
|
458
|
+
heading_text: heading_text,
|
|
459
|
+
heading_source: heading_source,
|
|
460
|
+
level: node.header_level,
|
|
461
|
+
base: normalize_heading_base(heading_text)
|
|
462
|
+
)
|
|
463
|
+
rescue StandardError
|
|
464
|
+
nil
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def branch_end_line(headings, index)
|
|
468
|
+
current = headings[index]
|
|
469
|
+
cursor = index + 1
|
|
470
|
+
while cursor < headings.length
|
|
471
|
+
return headings[cursor].location.start_line - 1 if headings[cursor].level <= current.level
|
|
472
|
+
|
|
473
|
+
cursor += 1
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
source.to_s.lines.length
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def unwrap_markdown_statement(statement)
|
|
480
|
+
Ast::Merge::NodeTyping.unwrap(statement)
|
|
481
|
+
rescue StandardError
|
|
482
|
+
statement
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def normalize_heading_base(text)
|
|
486
|
+
text.to_s.sub(/\A(?:\d\uFE0F?\u20E3|[^[:alnum:][:space:]])+[ \t]*/u, '').strip.downcase
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def inline_references_for_line(line, line_number)
|
|
490
|
+
owners = []
|
|
491
|
+
index = 0
|
|
492
|
+
while index < line.length
|
|
493
|
+
image = inline_image_reference_at(line, index, line_number)
|
|
494
|
+
if image
|
|
495
|
+
owners << image
|
|
496
|
+
index = image.end_column
|
|
497
|
+
next
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
link = inline_link_reference_at(line, index, line_number)
|
|
501
|
+
if link
|
|
502
|
+
owners << link
|
|
503
|
+
index = link.end_column
|
|
504
|
+
next
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
index += 1
|
|
508
|
+
end
|
|
509
|
+
owners
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def inline_image_reference_at(line, index, line_number)
|
|
513
|
+
return unless line[index] == '!' && line[index + 1] == '['
|
|
514
|
+
|
|
515
|
+
alt_end = closing_bracket_index(line, index + 1)
|
|
516
|
+
return unless alt_end && line[alt_end + 1] == '['
|
|
517
|
+
|
|
518
|
+
label_end = closing_bracket_index(line, alt_end + 1)
|
|
519
|
+
return unless label_end
|
|
520
|
+
|
|
521
|
+
label = line[(alt_end + 2)...label_end]
|
|
522
|
+
inline_reference_owner(
|
|
523
|
+
line: line,
|
|
524
|
+
line_number: line_number,
|
|
525
|
+
start_column: index,
|
|
526
|
+
end_column: label_end + 1,
|
|
527
|
+
reference_kind: :image_reference,
|
|
528
|
+
label: label,
|
|
529
|
+
labels: [label]
|
|
530
|
+
)
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
def inline_link_reference_at(line, index, line_number)
|
|
534
|
+
return unless line[index] == '['
|
|
535
|
+
|
|
536
|
+
text_end = closing_bracket_index(line, index)
|
|
537
|
+
return unless text_end && line[text_end + 1] == '['
|
|
538
|
+
|
|
539
|
+
label_end = closing_bracket_index(line, text_end + 1)
|
|
540
|
+
return unless label_end
|
|
541
|
+
|
|
542
|
+
text = line[(index + 1)...text_end]
|
|
543
|
+
label = line[(text_end + 2)...label_end]
|
|
544
|
+
image_owner = inline_image_reference_at(text, 0, line_number)
|
|
545
|
+
labels = [label]
|
|
546
|
+
labels.unshift(image_owner.label) if image_owner && image_owner.source == text
|
|
547
|
+
inline_reference_owner(
|
|
548
|
+
line: line,
|
|
549
|
+
line_number: line_number,
|
|
550
|
+
start_column: index,
|
|
551
|
+
end_column: label_end + 1,
|
|
552
|
+
reference_kind: labels.length > 1 ? :linked_image_reference : :link_reference,
|
|
553
|
+
label: label,
|
|
554
|
+
labels: labels
|
|
555
|
+
)
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
def inline_reference_owner(line:, line_number:, start_column:, end_column:, reference_kind:, label:, labels:)
|
|
559
|
+
InlineReferenceOwner.new(
|
|
560
|
+
location: Location.new(start_line: line_number, end_line: line_number),
|
|
561
|
+
line: line_number,
|
|
562
|
+
start_column: start_column,
|
|
563
|
+
end_column: end_column,
|
|
564
|
+
source: line[start_column...end_column],
|
|
565
|
+
reference_kind: reference_kind,
|
|
566
|
+
label: label,
|
|
567
|
+
labels: labels.compact.uniq
|
|
568
|
+
)
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def closing_bracket_index(text, opening_index)
|
|
572
|
+
depth = 0
|
|
573
|
+
index = opening_index
|
|
574
|
+
while index < text.length
|
|
575
|
+
case text[index]
|
|
576
|
+
when '['
|
|
577
|
+
depth += 1
|
|
578
|
+
when ']'
|
|
579
|
+
depth -= 1
|
|
580
|
+
return index if depth.zero?
|
|
581
|
+
end
|
|
582
|
+
index += 1
|
|
583
|
+
end
|
|
584
|
+
nil
|
|
585
|
+
end
|
|
586
|
+
|
|
256
587
|
# Recursively collect text content from a node and its descendants.
|
|
257
588
|
#
|
|
258
589
|
# Uses NodeTypeNormalizer to map backend-specific types to canonical types,
|
|
@@ -271,14 +602,14 @@ module Markdown
|
|
|
271
602
|
canonical_type = NodeTypeNormalizer.canonical_type(node.type, @backend)
|
|
272
603
|
|
|
273
604
|
# Collect text from text and code nodes
|
|
274
|
-
if
|
|
605
|
+
if %i[text code].include?(canonical_type)
|
|
275
606
|
content = if node.respond_to?(:string_content)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
607
|
+
node.string_content.to_s
|
|
608
|
+
elsif node.respond_to?(:text)
|
|
609
|
+
node.text.to_s
|
|
610
|
+
else
|
|
611
|
+
''
|
|
612
|
+
end
|
|
282
613
|
text_parts << content unless content.empty?
|
|
283
614
|
end
|
|
284
615
|
|
|
@@ -290,63 +621,68 @@ module Markdown
|
|
|
290
621
|
|
|
291
622
|
# Resolve the backend to use.
|
|
292
623
|
#
|
|
293
|
-
# For :auto,
|
|
294
|
-
# tree_haver handles the
|
|
624
|
+
# For :auto, use the same backend selection as the Markdown substrate facade.
|
|
625
|
+
# tree_haver handles the final availability checking.
|
|
295
626
|
#
|
|
296
627
|
# @param backend [Symbol] Requested backend
|
|
297
|
-
# @return [Symbol] Resolved backend
|
|
628
|
+
# @return [Symbol] Resolved backend
|
|
298
629
|
def resolve_backend(backend)
|
|
299
|
-
return backend
|
|
630
|
+
return Markdown::Merge.resolve_backend(nil).to_sym if backend.to_s.empty? || backend == :auto
|
|
300
631
|
|
|
301
|
-
|
|
302
|
-
if TreeHaver::BackendRegistry.available?(:commonmarker)
|
|
303
|
-
:commonmarker
|
|
304
|
-
elsif TreeHaver::BackendRegistry.available?(:markly)
|
|
305
|
-
:markly
|
|
306
|
-
else
|
|
307
|
-
# Let tree_haver raise the appropriate error
|
|
308
|
-
:commonmarker
|
|
309
|
-
end
|
|
632
|
+
backend.to_sym
|
|
310
633
|
end
|
|
311
634
|
|
|
312
635
|
# Create a parser for the resolved backend.
|
|
313
636
|
#
|
|
314
637
|
# @return [Object] tree_haver parser instance
|
|
315
638
|
def create_parser
|
|
639
|
+
unless Markdown::Merge::BACKEND_REFERENCES.key?(@backend.to_s)
|
|
640
|
+
raise ArgumentError, "Unknown backend: #{@backend}"
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
parser = TreeHaver.with_backend(@backend) { TreeHaver.parser_for(:markdown) }
|
|
644
|
+
|
|
316
645
|
case @backend
|
|
317
646
|
when :commonmarker
|
|
318
|
-
|
|
647
|
+
parser.language = commonmarker_language
|
|
319
648
|
when :markly
|
|
320
|
-
|
|
649
|
+
parser.language = markly_language
|
|
650
|
+
when :kramdown
|
|
651
|
+
parser.language = kramdown_language
|
|
321
652
|
else
|
|
322
|
-
|
|
653
|
+
return parser
|
|
323
654
|
end
|
|
655
|
+
|
|
656
|
+
parser
|
|
324
657
|
end
|
|
325
658
|
|
|
326
|
-
# Create a Commonmarker
|
|
659
|
+
# Create a Commonmarker language config for the TreeHaver parser.
|
|
327
660
|
#
|
|
328
|
-
# @return [Commonmarker::Merge::Backend::
|
|
329
|
-
def
|
|
330
|
-
parser = Commonmarker::Merge::Backend::Parser.new
|
|
661
|
+
# @return [Commonmarker::Merge::Backend::Language]
|
|
662
|
+
def commonmarker_language
|
|
331
663
|
# Default options enable table extension for GFM compatibility
|
|
332
|
-
default_options = {extension: {table: true}}
|
|
664
|
+
default_options = { extension: { table: true } }
|
|
333
665
|
options = default_options.merge(@parser_options[:options] || {})
|
|
334
|
-
|
|
335
|
-
parser
|
|
666
|
+
Commonmarker::Merge::Backend::Language.markdown(options: options)
|
|
336
667
|
end
|
|
337
668
|
|
|
338
|
-
# Create a Markly
|
|
669
|
+
# Create a Markly language config for the TreeHaver parser.
|
|
339
670
|
#
|
|
340
|
-
# @return [Markly::Merge::Backend::
|
|
341
|
-
def
|
|
342
|
-
parser = Markly::Merge::Backend::Parser.new
|
|
671
|
+
# @return [Markly::Merge::Backend::Language]
|
|
672
|
+
def markly_language
|
|
343
673
|
flags = @parser_options[:flags]
|
|
344
674
|
extensions = @parser_options[:extensions] || [:table]
|
|
345
|
-
|
|
675
|
+
Markly::Merge::Backend::Language.markdown(
|
|
346
676
|
flags: flags,
|
|
347
|
-
extensions: extensions
|
|
677
|
+
extensions: extensions
|
|
348
678
|
)
|
|
349
|
-
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# Create a Kramdown language config for the TreeHaver parser.
|
|
682
|
+
#
|
|
683
|
+
# @return [Kramdown::Merge::Backend::Language]
|
|
684
|
+
def kramdown_language
|
|
685
|
+
Kramdown::Merge::Backend::Language.markdown(options: @parser_options[:options] || {})
|
|
350
686
|
end
|
|
351
687
|
end
|
|
352
688
|
end
|