Changeset View
Changeset View
Standalone View
Standalone View
src/applications/differential/parser/DifferentialHunkParser.php
<?php | <?php | ||||
final class DifferentialHunkParser extends Phobject { | final class DifferentialHunkParser extends Phobject { | ||||
private $oldLines; | private $oldLines; | ||||
private $newLines; | private $newLines; | ||||
private $intraLineDiffs; | private $intraLineDiffs; | ||||
private $depthOnlyLines; | private $depthOnlyLines; | ||||
private $visibleLinesMask; | private $visibleLinesMask; | ||||
private $whitespaceMode; | |||||
/** | /** | ||||
* Get a map of lines on which hunks start, other than line 1. This | * Get a map of lines on which hunks start, other than line 1. This | ||||
* datastructure is used to determine when to render "Context not available." | * datastructure is used to determine when to render "Context not available." | ||||
* in diffs with multiple hunks. | * in diffs with multiple hunks. | ||||
* | * | ||||
* @return dict<int, bool> Map of lines where hunks start, other than line 1. | * @return dict<int, bool> Map of lines where hunks start, other than line 1. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | public function setDepthOnlyLines(array $map) { | ||||
$this->depthOnlyLines = $map; | $this->depthOnlyLines = $map; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getDepthOnlyLines() { | public function getDepthOnlyLines() { | ||||
return $this->depthOnlyLines; | return $this->depthOnlyLines; | ||||
} | } | ||||
public function setWhitespaceMode($white_space_mode) { | |||||
$this->whitespaceMode = $white_space_mode; | |||||
return $this; | |||||
} | |||||
private function getWhitespaceMode() { | |||||
if ($this->whitespaceMode === null) { | |||||
throw new Exception( | |||||
pht( | |||||
'You must %s before accessing this data.', | |||||
'setWhitespaceMode')); | |||||
} | |||||
return $this->whitespaceMode; | |||||
} | |||||
public function getIsDeleted() { | public function getIsDeleted() { | ||||
foreach ($this->getNewLines() as $line) { | foreach ($this->getNewLines() as $line) { | ||||
if ($line) { | if ($line) { | ||||
// At least one new line, so the entire file wasn't deleted. | // At least one new line, so the entire file wasn't deleted. | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
foreach ($this->getOldLines() as $line) { | foreach ($this->getOldLines() as $line) { | ||||
if ($line) { | if ($line) { | ||||
// No new lines, at least one old line; the entire file was deleted. | // No new lines, at least one old line; the entire file was deleted. | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
// This is an empty file. | // This is an empty file. | ||||
return false; | return false; | ||||
} | } | ||||
/** | /** | ||||
* Returns true if the hunks change any text, not just whitespace. | |||||
*/ | |||||
public function getHasTextChanges() { | |||||
return $this->getHasChanges('text'); | |||||
} | |||||
/** | |||||
* Returns true if the hunks change anything, including whitespace. | * Returns true if the hunks change anything, including whitespace. | ||||
*/ | */ | ||||
public function getHasAnyChanges() { | public function getHasAnyChanges() { | ||||
return $this->getHasChanges('any'); | return $this->getHasChanges('any'); | ||||
} | } | ||||
private function getHasChanges($filter) { | private function getHasChanges($filter) { | ||||
if ($filter !== 'any' && $filter !== 'text') { | if ($filter !== 'any' && $filter !== 'text') { | ||||
Show All 10 Lines | foreach ($old as $key => $o) { | ||||
if ($o === null || $n === null) { | if ($o === null || $n === null) { | ||||
// One side is missing, and it's impossible for both sides to be null, | // One side is missing, and it's impossible for both sides to be null, | ||||
// so the other side must have something, and thus the two sides are | // so the other side must have something, and thus the two sides are | ||||
// different and the file has been changed under any type of filter. | // different and the file has been changed under any type of filter. | ||||
return true; | return true; | ||||
} | } | ||||
if ($o['type'] !== $n['type']) { | if ($o['type'] !== $n['type']) { | ||||
// The types are different, so either the underlying text is actually | |||||
// different or whatever whitespace rules we're using consider them | |||||
// different. | |||||
return true; | return true; | ||||
} | } | ||||
if ($o['text'] !== $n['text']) { | if ($o['text'] !== $n['text']) { | ||||
if ($is_any) { | if ($is_any) { | ||||
// The text is different, so there's a change. | // The text is different, so there's a change. | ||||
return true; | return true; | ||||
} else if (trim($o['text']) !== trim($n['text'])) { | } else if (trim($o['text']) !== trim($n['text'])) { | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | while (count($old_lines) || count($new_lines)) { | ||||
$rebuild_old[] = $old_line_data; | $rebuild_old[] = $old_line_data; | ||||
$rebuild_new[] = $new_line_data; | $rebuild_new[] = $new_line_data; | ||||
} | } | ||||
$this->setOldLines($rebuild_old); | $this->setOldLines($rebuild_old); | ||||
$this->setNewLines($rebuild_new); | $this->setNewLines($rebuild_new); | ||||
$this->updateChangeTypesForWhitespaceMode(); | |||||
return $this; | |||||
} | |||||
private function updateChangeTypesForWhitespaceMode() { | |||||
$mode = $this->getWhitespaceMode(); | |||||
$mode_show_all = DifferentialChangesetParser::WHITESPACE_SHOW_ALL; | |||||
if ($mode === $mode_show_all) { | |||||
// If we're showing all whitespace, we don't need to perform any updates. | |||||
return; | |||||
} | |||||
$mode_trailing = DifferentialChangesetParser::WHITESPACE_IGNORE_TRAILING; | |||||
$is_trailing = ($mode === $mode_trailing); | |||||
$new = $this->getNewLines(); | |||||
$old = $this->getOldLines(); | |||||
foreach ($old as $key => $o) { | |||||
$n = $new[$key]; | |||||
if (!$o || !$n) { | |||||
continue; | |||||
} | |||||
if ($is_trailing) { | |||||
// In "trailing" mode, we need to identify lines which are marked | |||||
// changed but differ only by trailing whitespace. We mark these lines | |||||
// unchanged. | |||||
if ($o['type'] != $n['type']) { | |||||
if (rtrim($o['text']) === rtrim($n['text'])) { | |||||
$old[$key]['type'] = null; | |||||
$new[$key]['type'] = null; | |||||
} | |||||
} | |||||
} else { | |||||
// In "ignore most" and "ignore all" modes, we need to identify lines | |||||
// which are marked unchanged but have internal whitespace changes. | |||||
// We want to ignore leading and trailing whitespace changes only, not | |||||
// internal whitespace changes (`diff` doesn't have a mode for this, so | |||||
// we have to fix it here). If the text is marked unchanged but the | |||||
// old and new text differs by internal space, mark the lines changed. | |||||
if ($o['type'] === null && $n['type'] === null) { | |||||
if ($o['text'] !== $n['text']) { | |||||
if (trim($o['text']) !== trim($n['text'])) { | |||||
$old[$key]['type'] = '-'; | |||||
$new[$key]['type'] = '+'; | |||||
} | |||||
} | |||||
} | |||||
epriestley: This piece may need to come back, too. | |||||
} | |||||
} | |||||
$this->setOldLines($old); | |||||
$this->setNewLines($new); | |||||
return $this; | return $this; | ||||
} | } | ||||
public function generateIntraLineDiffs() { | public function generateIntraLineDiffs() { | ||||
$old = $this->getOldLines(); | $old = $this->getOldLines(); | ||||
$new = $this->getNewLines(); | $new = $this->getNewLines(); | ||||
$diffs = array(); | $diffs = array(); | ||||
▲ Show 20 Lines • Show All 494 Lines • Show Last 20 Lines |
This piece may need to come back, too.