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
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Markdown
|
|
4
|
+
module Merge
|
|
5
|
+
# Conservatively tracks standalone HTML comment lines in Markdown sources.
|
|
6
|
+
class CommentTracker < Ast::Merge::Comment::HashTrackerBase
|
|
7
|
+
STANDALONE_HTML_COMMENT_REGEX = /\A(?<indent>\s*)<!--\s?(?<text>.*?)\s?-->\s*\z/
|
|
8
|
+
|
|
9
|
+
def initialize(lines)
|
|
10
|
+
super(Array(lines))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def comment_style
|
|
16
|
+
:html_comment
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def extract_comments
|
|
20
|
+
@lines.each_with_index.filter_map do |line, index|
|
|
21
|
+
match = line.match(STANDALONE_HTML_COMMENT_REGEX)
|
|
22
|
+
next unless match
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
line: index + 1,
|
|
26
|
+
indent: match[:indent].length,
|
|
27
|
+
text: match[:text].to_s,
|
|
28
|
+
full_line: true,
|
|
29
|
+
raw: line
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def owner_line_num(owner)
|
|
35
|
+
pos = owner.respond_to?(:source_position) ? owner.source_position : nil
|
|
36
|
+
return pos[:start_line] if pos && pos[:start_line]
|
|
37
|
+
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -34,6 +34,8 @@ module Markdown
|
|
|
34
34
|
# @param dest_analysis [FileAnalysisBase] Analysis of the destination file
|
|
35
35
|
# @param options [Hash] Additional options for forward compatibility
|
|
36
36
|
def initialize(preference:, template_analysis:, dest_analysis:, **options)
|
|
37
|
+
@resolution_mode = options.fetch(:resolution_mode, :eager)
|
|
38
|
+
@unresolved_policy = Ast::Merge::UnresolvedPolicy.coerce(options[:unresolved_policy])
|
|
37
39
|
super(
|
|
38
40
|
strategy: :node,
|
|
39
41
|
preference: preference,
|
|
@@ -59,7 +61,7 @@ module Markdown
|
|
|
59
61
|
source: :destination,
|
|
60
62
|
template_node: template_node,
|
|
61
63
|
dest_node: dest_node,
|
|
62
|
-
reason: dest_node.reason
|
|
64
|
+
reason: dest_node.reason
|
|
63
65
|
)
|
|
64
66
|
end
|
|
65
67
|
|
|
@@ -68,7 +70,7 @@ module Markdown
|
|
|
68
70
|
source: :template,
|
|
69
71
|
template_node: template_node,
|
|
70
72
|
dest_node: dest_node,
|
|
71
|
-
reason: template_node.reason
|
|
73
|
+
reason: template_node.reason
|
|
72
74
|
)
|
|
73
75
|
end
|
|
74
76
|
|
|
@@ -76,14 +78,21 @@ module Markdown
|
|
|
76
78
|
if content_identical?(template_node, dest_node)
|
|
77
79
|
return identical_resolution(
|
|
78
80
|
template_node: template_node,
|
|
79
|
-
dest_node: dest_node
|
|
81
|
+
dest_node: dest_node
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if unresolved_mode? && @unresolved_policy.unresolved_for?(:matched_block)
|
|
86
|
+
return unresolved_resolution(
|
|
87
|
+
template_node: template_node,
|
|
88
|
+
dest_node: dest_node
|
|
80
89
|
)
|
|
81
90
|
end
|
|
82
91
|
|
|
83
92
|
# Use preference to decide
|
|
84
93
|
preference_resolution(
|
|
85
94
|
template_node: template_node,
|
|
86
|
-
dest_node: dest_node
|
|
95
|
+
dest_node: dest_node
|
|
87
96
|
)
|
|
88
97
|
end
|
|
89
98
|
|
|
@@ -100,6 +109,50 @@ module Markdown
|
|
|
100
109
|
template_text == dest_text
|
|
101
110
|
end
|
|
102
111
|
|
|
112
|
+
def unresolved_resolution(template_node:, dest_node:)
|
|
113
|
+
preferred_resolution = preference_resolution(template_node: template_node, dest_node: dest_node)
|
|
114
|
+
provisional_winner = @unresolved_policy.provisional_winner_for(
|
|
115
|
+
:matched_block,
|
|
116
|
+
fallback: preferred_resolution[:source]
|
|
117
|
+
)
|
|
118
|
+
line = node_line_range(dest_node)&.first || node_line_range(template_node)&.first
|
|
119
|
+
case_id = ['markdown', 'matched_block', line].compact.join('-')
|
|
120
|
+
surface_path = unresolved_surface_path("matched_block[line=#{line}]")
|
|
121
|
+
|
|
122
|
+
unresolved_case = Ast::Merge::Runtime::ResolutionCase.new(
|
|
123
|
+
case_id: case_id,
|
|
124
|
+
reason: :conflict,
|
|
125
|
+
candidates: {
|
|
126
|
+
template: node_to_text(template_node, @template_analysis),
|
|
127
|
+
destination: node_to_text(dest_node, @dest_analysis)
|
|
128
|
+
},
|
|
129
|
+
provisional_winner: provisional_winner,
|
|
130
|
+
surface_path: surface_path,
|
|
131
|
+
metadata: {
|
|
132
|
+
match_kind: :matched_block,
|
|
133
|
+
template_lines: node_line_range(template_node),
|
|
134
|
+
destination_lines: node_line_range(dest_node),
|
|
135
|
+
node_type: markdown_node_type(dest_node || template_node)
|
|
136
|
+
}.compact
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
{
|
|
140
|
+
source: provisional_winner,
|
|
141
|
+
decision: Ast::Merge::MergeResultBase::DECISION_UNRESOLVED,
|
|
142
|
+
template_node: template_node,
|
|
143
|
+
dest_node: dest_node,
|
|
144
|
+
unresolved_case: unresolved_case,
|
|
145
|
+
conflict: {
|
|
146
|
+
case_id: case_id,
|
|
147
|
+
reason: :conflict,
|
|
148
|
+
template: unresolved_case.candidates[:template],
|
|
149
|
+
destination: unresolved_case.candidates[:destination],
|
|
150
|
+
provisional_winner: provisional_winner,
|
|
151
|
+
location: line ? "line #{line}" : nil
|
|
152
|
+
}.compact
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
|
|
103
156
|
# Convert a node to its source text
|
|
104
157
|
#
|
|
105
158
|
# @param node [Object] Node to convert
|
|
@@ -117,12 +170,30 @@ module Markdown
|
|
|
117
170
|
if start_line && end_line
|
|
118
171
|
analysis.source_range(start_line, end_line)
|
|
119
172
|
else
|
|
120
|
-
# :
|
|
173
|
+
# simplecov:disable defensive - Markdown nodes typically have source positions
|
|
121
174
|
node.to_commonmark
|
|
122
|
-
# :
|
|
175
|
+
# simplecov:enable
|
|
123
176
|
end
|
|
124
177
|
end
|
|
125
178
|
end
|
|
179
|
+
|
|
180
|
+
def node_line_range(node)
|
|
181
|
+
if node.respond_to?(:source_position)
|
|
182
|
+
pos = node.source_position
|
|
183
|
+
start_line = pos&.dig(:start_line)
|
|
184
|
+
end_line = pos&.dig(:end_line)
|
|
185
|
+
return [start_line, end_line] if start_line && end_line
|
|
186
|
+
end
|
|
187
|
+
return [node.start_line, node.end_line] if node.respond_to?(:start_line) && node.respond_to?(:end_line)
|
|
188
|
+
|
|
189
|
+
nil
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def markdown_node_type(node)
|
|
193
|
+
return node.type.to_sym if node.respond_to?(:type) && !node.type.nil?
|
|
194
|
+
|
|
195
|
+
node.class.name.split('::').last
|
|
196
|
+
end
|
|
126
197
|
end
|
|
127
198
|
end
|
|
128
199
|
end
|
|
@@ -19,8 +19,8 @@ module Markdown
|
|
|
19
19
|
extend Ast::Merge::DebugLogger
|
|
20
20
|
|
|
21
21
|
# Configure for markdown-merge
|
|
22
|
-
self.env_var_name =
|
|
23
|
-
self.log_prefix =
|
|
22
|
+
self.env_var_name = 'MARKDOWN_MERGE_DEBUG'
|
|
23
|
+
self.log_prefix = '[markdown-merge]'
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -28,7 +28,7 @@ module Markdown
|
|
|
28
28
|
# Problem entry struct
|
|
29
29
|
Problem = Struct.new(:category, :severity, :details, keyword_init: true) do
|
|
30
30
|
def to_h
|
|
31
|
-
{category: category, severity: severity, **details}
|
|
31
|
+
{ category: category, severity: severity, **details }
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def warning?
|
|
@@ -177,13 +177,13 @@ module Markdown
|
|
|
177
177
|
def validate_category!(category)
|
|
178
178
|
return if CATEGORIES.include?(category)
|
|
179
179
|
|
|
180
|
-
raise ArgumentError, "Invalid category: #{category}. Valid: #{CATEGORIES.join(
|
|
180
|
+
raise ArgumentError, "Invalid category: #{category}. Valid: #{CATEGORIES.join(', ')}"
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
def validate_severity!(severity)
|
|
184
184
|
return if SEVERITIES.include?(severity)
|
|
185
185
|
|
|
186
|
-
raise ArgumentError, "Invalid severity: #{severity}. Valid: #{SEVERITIES.join(
|
|
186
|
+
raise ArgumentError, "Invalid severity: #{severity}. Valid: #{SEVERITIES.join(', ')}"
|
|
187
187
|
end
|
|
188
188
|
end
|
|
189
189
|
end
|