Create a list of diff hunks in Unified diff format. Each hunk has a header generated by PrintHeader above plus a body with lines prefixed with ' ' for no change, '-' for deletion and '+' for addition. 'context' represents the desired unchanged prefix/suffix around the diff. If two hunks are close enough that their contexts overlap, then they are joined into one hunk.
| 2694 | // If two hunks are close enough that their contexts overlap, then they are |
| 2695 | // joined into one hunk. |
| 2696 | std::string CreateUnifiedDiff(const std::vector<std::string>& left, |
| 2697 | const std::vector<std::string>& right, |
| 2698 | size_t context) { |
| 2699 | const std::vector<EditType> edits = CalculateOptimalEdits(left, right); |
| 2700 | |
| 2701 | size_t l_i = 0, r_i = 0, edit_i = 0; |
| 2702 | std::stringstream ss; |
| 2703 | while (edit_i < edits.size()) { |
| 2704 | // Find first edit. |
| 2705 | while (edit_i < edits.size() && edits[edit_i] == kMatch) { |
| 2706 | ++l_i; |
| 2707 | ++r_i; |
| 2708 | ++edit_i; |
| 2709 | } |
| 2710 | |
| 2711 | // Find the first line to include in the hunk. |
| 2712 | const size_t prefix_context = std::min(l_i, context); |
| 2713 | Hunk hunk(l_i - prefix_context + 1, r_i - prefix_context + 1); |
| 2714 | for (size_t i = prefix_context; i > 0; --i) { |
| 2715 | hunk.PushLine(' ', left[l_i - i].c_str()); |
| 2716 | } |
| 2717 | |
| 2718 | // Iterate the edits until we found enough suffix for the hunk or the input |
| 2719 | // is over. |
| 2720 | size_t n_suffix = 0; |
| 2721 | for (; edit_i < edits.size(); ++edit_i) { |
| 2722 | if (n_suffix >= context) { |
| 2723 | // Continue only if the next hunk is very close. |
| 2724 | auto it = edits.begin() + static_cast<int>(edit_i); |
| 2725 | while (it != edits.end() && *it == kMatch) ++it; |
| 2726 | if (it == edits.end() || |
| 2727 | static_cast<size_t>(it - edits.begin()) - edit_i >= context) { |
| 2728 | // There is no next edit or it is too far away. |
| 2729 | break; |
| 2730 | } |
| 2731 | } |
| 2732 | |
| 2733 | EditType edit = edits[edit_i]; |
| 2734 | // Reset count when a non match is found. |
| 2735 | n_suffix = edit == kMatch ? n_suffix + 1 : 0; |
| 2736 | |
| 2737 | if (edit == kMatch || edit == kRemove || edit == kReplace) { |
| 2738 | hunk.PushLine(edit == kMatch ? ' ' : '-', left[l_i].c_str()); |
| 2739 | } |
| 2740 | if (edit == kAdd || edit == kReplace) { |
| 2741 | hunk.PushLine('+', right[r_i].c_str()); |
| 2742 | } |
| 2743 | |
| 2744 | // Advance indices, depending on edit type. |
| 2745 | l_i += edit != kAdd; |
| 2746 | r_i += edit != kRemove; |
| 2747 | } |
| 2748 | |
| 2749 | if (!hunk.has_edits()) { |
| 2750 | // We are done. We don't want this hunk. |
| 2751 | break; |
| 2752 | } |
| 2753 |