public function cleanupSemanticLossless()
{
$diffs = $this->getChanges();
$pointer = 1;
// Intentionally ignore the first and last element (don't need checking).
while ($pointer < count($diffs) - 1) {
if ($diffs[$pointer - 1][0] == self::EQUAL && $diffs[$pointer + 1][0] == self::EQUAL) {
// This is a single edit surrounded by equalities.
$equality1 = $diffs[$pointer - 1][1];
$edit = $diffs[$pointer][1];
$equality2 = $diffs[$pointer + 1][1];
// First, shift the edit as far left as possible.
$commonOffset = $this->getToolkit()->commonSuffix($equality1, $edit);
if ($commonOffset) {
$commonString = mb_substr($edit, -$commonOffset);
$equality1 = mb_substr($equality1, 0, -$commonOffset);
$edit = $commonString . mb_substr($edit, 0, -$commonOffset);
$equality2 = $commonString . $equality2;
}
// Second, step character by character right, looking for the best fit.
$bestEquality1 = $equality1;
$bestEdit = $edit;
$bestEquality2 = $equality2;
$bestScore = $this->cleanupSemanticScore($equality1, $edit) + $this->cleanupSemanticScore($edit, $equality2);
while ($edit && $equality2 && mb_substr($edit, 0, 1) == mb_substr($equality2, 0, 1)) {
$equality1 .= mb_substr($edit, 0, 1);
$edit = mb_substr($edit, 1) . mb_substr($equality2, 0, 1);
$equality2 = mb_substr($equality2, 1);
$score = $this->cleanupSemanticScore($equality1, $edit) + $this->cleanupSemanticScore($edit, $equality2);
// The >= encourages trailing rather than leading whitespace on edits.
if ($score >= $bestScore) {
$bestScore = $score;
$bestEquality1 = $equality1;
$bestEdit = $edit;
$bestEquality2 = $equality2;
}
}
if ($diffs[$pointer - 1][1] != $bestEquality1) {
// We have an improvement, save it back to the diff.
if ($bestEquality1 != '') {
$diffs[$pointer - 1][1] = $bestEquality1;
} else {
array_splice($diffs, $pointer - 1, 1);
$pointer -= 1;
}
$diffs[$pointer][1] = $bestEdit;
if ($bestEquality2 != '') {
$diffs[$pointer + 1][1] = $bestEquality2;
} else {
array_splice($diffs, $pointer + 1, 1);
$pointer -= 1;
}
}
}
$pointer++;
}
$this->setChanges($diffs);
return $this;
}