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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE.md +13 -0
  4. data/README.md +99 -482
  5. data/lib/markdown/merge/backend_support.rb +200 -0
  6. data/lib/markdown/merge/cleanse/block_spacing.rb +18 -23
  7. data/lib/markdown/merge/cleanse/code_fence_spacing.rb +16 -16
  8. data/lib/markdown/merge/cleanse/condensed_link_refs.rb +36 -30
  9. data/lib/markdown/merge/cleanse/list_marker_duplication.rb +66 -0
  10. data/lib/markdown/merge/cleanse/templating_corruption.rb +86 -0
  11. data/lib/markdown/merge/cleanse.rb +5 -3
  12. data/lib/markdown/merge/code_block_match_refiner.rb +111 -0
  13. data/lib/markdown/merge/code_block_merger.rb +489 -47
  14. data/lib/markdown/merge/comment_tracker.rb +42 -0
  15. data/lib/markdown/merge/conflict_resolver.rb +77 -6
  16. data/lib/markdown/merge/debug_logger.rb +2 -2
  17. data/lib/markdown/merge/document_problems.rb +3 -3
  18. data/lib/markdown/merge/file_aligner.rb +433 -133
  19. data/lib/markdown/merge/file_analysis.rb +387 -51
  20. data/lib/markdown/merge/file_analysis_base.rb +188 -51
  21. data/lib/markdown/merge/gap_line_node.rb +14 -8
  22. data/lib/markdown/merge/link_definition_node.rb +5 -5
  23. data/lib/markdown/merge/link_parser.rb +60 -60
  24. data/lib/markdown/merge/link_reference_rehydrator.rb +14 -14
  25. data/lib/markdown/merge/list_match_refiner.rb +98 -0
  26. data/lib/markdown/merge/list_merger.rb +322 -0
  27. data/lib/markdown/merge/markdown_structure.rb +3 -3
  28. data/lib/markdown/merge/merge_result.rb +321 -4
  29. data/lib/markdown/merge/node_type_normalizer.rb +6 -6
  30. data/lib/markdown/merge/output_builder.rb +101 -19
  31. data/lib/markdown/merge/partial_template_merger.rb +248 -27
  32. data/lib/markdown/merge/preservation_support.rb +291 -0
  33. data/lib/markdown/merge/smart_merger.rb +62 -14
  34. data/lib/markdown/merge/smart_merger_base.rb +929 -60
  35. data/lib/markdown/merge/table_match_algorithm.rb +22 -27
  36. data/lib/markdown/merge/table_match_refiner.rb +6 -10
  37. data/lib/markdown/merge/version.rb +5 -4
  38. data/lib/markdown/merge/whitespace_normalizer.rb +25 -33
  39. data/lib/markdown/merge/wrapper_support.rb +194 -0
  40. data/lib/markdown/merge.rb +669 -122
  41. data/lib/markdown-merge.rb +9 -4
  42. data/sig/markdown/merge.rbs +3 -336
  43. data.tar.gz.sig +0 -0
  44. metadata +104 -93
  45. metadata.gz.sig +0 -0
  46. data/CHANGELOG.md +0 -308
  47. data/CITATION.cff +0 -20
  48. data/CODE_OF_CONDUCT.md +0 -134
  49. data/CONTRIBUTING.md +0 -227
  50. data/FUNDING.md +0 -74
  51. data/LICENSE.txt +0 -21
  52. data/REEK +0 -0
  53. data/RUBOCOP.md +0 -71
  54. 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
- # :nocov: defensive - Markdown nodes typically have source positions
173
+ # simplecov:disable defensive - Markdown nodes typically have source positions
121
174
  node.to_commonmark
122
- # :nocov:
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 = "MARKDOWN_MERGE_DEBUG"
23
- self.log_prefix = "[markdown-merge]"
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