Applies UPDATE chunks to the given text content. Adapted from _get_updated_file in apply_patch.py.
(self, text: str, action: PatchAction, path: str)
| 640 | ) |
| 641 | |
| 642 | def _apply_update(self, text: str, action: PatchAction, path: str) -> str: |
| 643 | """ |
| 644 | Applies UPDATE chunks to the given text content. |
| 645 | Adapted from _get_updated_file in apply_patch.py. |
| 646 | """ |
| 647 | if action.type is not ActionType.UPDATE: |
| 648 | # Should not be called otherwise, but check for safety |
| 649 | raise DiffError("_apply_update called with non-update action") |
| 650 | |
| 651 | orig_lines = text.splitlines() # Use splitlines to handle endings consistently |
| 652 | dest_lines: List[str] = [] |
| 653 | current_orig_line_idx = 0 # Tracks index in orig_lines processed so far |
| 654 | |
| 655 | # Sort chunks by their original index to apply them sequentially |
| 656 | sorted_chunks = sorted(action.chunks, key=lambda c: c.orig_index) |
| 657 | |
| 658 | for chunk in sorted_chunks: |
| 659 | # chunk.orig_index is the absolute line number where the change starts |
| 660 | # (where the first deleted line was, or where inserted lines go if no deletes) |
| 661 | chunk_start_index = chunk.orig_index |
| 662 | |
| 663 | if chunk_start_index < current_orig_line_idx: |
| 664 | # This indicates overlapping chunks or incorrect indices from parsing |
| 665 | raise DiffError( |
| 666 | f"{path}: Overlapping or out-of-order chunk detected." |
| 667 | f" Current index {current_orig_line_idx}, chunk starts at {chunk_start_index}." |
| 668 | ) |
| 669 | |
| 670 | # Add lines from original file between the last chunk and this one |
| 671 | dest_lines.extend(orig_lines[current_orig_line_idx:chunk_start_index]) |
| 672 | |
| 673 | # Verify that the lines to be deleted actually match the original file content |
| 674 | # (The parser should have used find_context, but double-check here) |
| 675 | num_del = len(chunk.del_lines) |
| 676 | actual_deleted_lines = orig_lines[chunk_start_index : chunk_start_index + num_del] |
| 677 | |
| 678 | # Use the same normalization as find_context_core for comparison robustness |
| 679 | norm_chunk_del = [_norm(s).strip() for s in chunk.del_lines] |
| 680 | norm_actual_del = [_norm(s).strip() for s in actual_deleted_lines] |
| 681 | |
| 682 | if norm_chunk_del != norm_actual_del: |
| 683 | # This indicates the context matching failed or the file changed since parsing |
| 684 | # Provide detailed error message |
| 685 | expected_str = "\n".join(f"- {s}" for s in chunk.del_lines) |
| 686 | actual_str = "\n".join(f" {s}" for s in actual_deleted_lines) |
| 687 | raise DiffError( |
| 688 | f"{path}: Mismatch applying patch near line {chunk_start_index + 1}.\n" |
| 689 | f"Expected lines to remove:\n{expected_str}\n" |
| 690 | f"Found lines in file:\n{actual_str}" |
| 691 | ) |
| 692 | |
| 693 | # Add the inserted lines from the chunk |
| 694 | dest_lines.extend(chunk.ins_lines) |
| 695 | |
| 696 | # Advance the original line index past the lines processed (deleted lines) |
| 697 | current_orig_line_idx = chunk_start_index + num_del |
| 698 | |
| 699 | # Add any remaining lines from the original file after the last chunk |
no test coverage detected