(&self, other: &Self)
| 217 | /// Panics if `other` comes before `self`. |
| 218 | #[must_use] |
| 219 | pub fn combine_with(&self, other: &Self) -> Self { |
| 220 | assert!( |
| 221 | other.old_start >= self.old_start, |
| 222 | "Cannot combine with a change that comes before" |
| 223 | ); |
| 224 | |
| 225 | // Calculate combined old range |
| 226 | let other_old_end = other.old_start + other.old_len; |
| 227 | let new_old_len = other_old_end.saturating_sub(self.old_start); |
| 228 | |
| 229 | // Calculate combined new range. |
| 230 | // |
| 231 | // This must span from the first change's new_start through the |
| 232 | // later change's new end, not merely sum the two changed ranges. |
| 233 | // When two changes are adjacent in the old file but separated in |
| 234 | // the new file by preserved or inserted lines, summing the lengths |
| 235 | // drops that middle section from the replacement hunk. The graph |
| 236 | // then records a patch that deletes a broad old range but only |
| 237 | // inserts the changed fragments, causing materialization to skip or |
| 238 | // rotate content after sequential inserts. |
| 239 | let self_new_end = self.new_start + self.new_len; |
| 240 | let other_new_end = other.new_start + other.new_len; |
| 241 | let combined_new_len = self_new_end |
| 242 | .max(other_new_end) |
| 243 | .saturating_sub(self.new_start); |
| 244 | |
| 245 | // Determine the combined kind |
| 246 | let kind = if new_old_len == 0 && combined_new_len > 0 { |
| 247 | PendingChangeKind::Insert |
| 248 | } else if combined_new_len == 0 && new_old_len > 0 { |
| 249 | PendingChangeKind::Delete |
| 250 | } else if new_old_len > 0 && combined_new_len > 0 { |
| 251 | PendingChangeKind::Replace |
| 252 | } else { |
| 253 | // Both are zero - shouldn't happen but default to delete |
| 254 | PendingChangeKind::Delete |
| 255 | }; |
| 256 | |
| 257 | Self { |
| 258 | kind, |
| 259 | old_start: self.old_start, |
| 260 | old_len: new_old_len, |
| 261 | new_start: self.new_start, |
| 262 | new_len: combined_new_len, |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | impl fmt::Display for PendingChange { |
no outgoing calls