Class: Markdown::Merge::ListMerger

Inherits:
Object
  • Object
show all
Includes:
Ast::Merge::JaccardSimilarity
Defined in:
lib/markdown/merge/list_merger.rb

Overview

Merges two Markdown list nodes at the item level.

When a template list and destination list are matched (e.g., via fuzzy matching or a shared content fingerprint), this merger produces a result that is smarter than simply picking one whole list as the winner:

- Items that appear in both lists are resolved by preference (template or dest).
- Items that only appear in the destination are kept (project customisations).
- Items that only appear in the template are added (new canonical steps).

Item matching uses significant-token Jaccard overlap so minor wording differences (e.g., "Commit changes" vs "Commit your changes") still produce a match.

The merged list is emitted as plain Markdown text (ordered 1. … lines) and passed to the caller via add_raw on the OutputBuilder.

Examples:

Basic usage

merger = ListMerger.new
result = merger.merge_lists(template_node, dest_node,
                            preference: :template,
                            add_template_only_nodes: true,
                            template_analysis: t_analysis,
                            dest_analysis: d_analysis)
if result[:merged]
  builder.add_raw(result[:content])
end

See Also:

  • SmartMergerBase#try_inner_merge_list_to_builder

Constant Summary collapse

ITEM_MATCH_THRESHOLD =

Minimum Jaccard token overlap to consider two list items as matching.

0.35

Instance Method Summary collapse

Instance Method Details

#merge_lists(template_node, dest_node, preference:, add_template_only_nodes: true, template_analysis: nil, dest_analysis: nil, resolution_mode: :eager, unresolved_policy: nil) ⇒ Hash

Merge two list nodes.

Parameters:

  • template_node (Object)

    Template list node (tree_haver / Markly node)

  • dest_node (Object)

    Destination list node

  • preference (Symbol)

    :template or :destination — which wins for matched items

  • add_template_only_nodes (Boolean) (defaults to: true)

    Whether to append template-only items

  • template_analysis (FileAnalysisBase) (defaults to: nil)

    Template file analysis (for source text)

  • dest_analysis (FileAnalysisBase) (defaults to: nil)

    Destination file analysis (for source text)

Returns:

  • (Hash)

    { merged: Boolean, content: String } or { merged: false, reason: String }



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/markdown/merge/list_merger.rb', line 48

def merge_lists(template_node, dest_node,
                preference:,
                add_template_only_nodes: true,
                template_analysis: nil,
                dest_analysis: nil,
                resolution_mode: :eager,
                unresolved_policy: nil)
  t_items = extract_items(template_node)
  d_items = extract_items(dest_node)

  return not_merged('empty list') if t_items.empty? && d_items.empty?

  alignment = align_items(t_items, d_items)
  lines, unresolved_cases = emit_lines(
    alignment,
    template_node: template_node,
    dest_node: dest_node,
    preference: preference,
    add_template_only: add_template_only_nodes,
    template_analysis: template_analysis,
    dest_analysis: dest_analysis,
    resolution_mode: resolution_mode,
    unresolved_policy: Ast::Merge::UnresolvedPolicy.coerce(unresolved_policy)
  )
  return not_merged('no lines emitted') if lines.empty?

  {
    merged: true,
    content: lines.join("\n") + "\n",
    stats: { decision: unresolved_cases.empty? ? :merged : :unresolved },
    unresolved_cases: unresolved_cases
  }
end