1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 02:02:41 +01:00
phorge-phorge/src/applications/differential/parser/changeset/DifferentialChangesetParser.php

1478 lines
42 KiB
PHP
Raw Normal View History

2011-01-25 00:52:35 +01:00
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class DifferentialChangesetParser {
protected $visible = array();
protected $new = array();
protected $old = array();
protected $intra = array();
protected $newRender = null;
protected $oldRender = null;
protected $parsedHunk = false;
protected $filename = null;
protected $filetype = null;
protected $changesetID = null;
protected $missingOld = array();
protected $missingNew = array();
protected $comments = array();
protected $specialAttributes = array();
protected $changeset;
protected $whitespaceMode = null;
protected $subparser;
protected $noHighlight;
2011-02-02 22:48:52 +01:00
protected $renderCacheKey = null;
2011-02-02 01:42:36 +01:00
private $handles;
2011-02-02 19:10:25 +01:00
private $user;
2011-01-25 00:52:35 +01:00
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
private $leftSideChangesetID;
private $leftSideAttachesToNewFile;
private $rightSideChangesetID;
private $rightSideAttachesToNewFile;
2011-01-25 00:52:35 +01:00
const CACHE_VERSION = 4;
const ATTR_GENERATED = 'attr:generated';
const ATTR_DELETED = 'attr:deleted';
const ATTR_UNCHANGED = 'attr:unchanged';
const ATTR_WHITELINES = 'attr:white';
const LINES_CONTEXT = 8;
const WHITESPACE_SHOW_ALL = 'show-all';
const WHITESPACE_IGNORE_TRAILING = 'ignore-trailing';
const WHITESPACE_IGNORE_ALL = 'ignore-all';
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
/**
* Configure which Changeset comments added to the right side of the visible
* diff will be attached to. The ID must be the ID of a real Differential
* Changeset.
*
* The complexity here is that we may show an arbitrary side of an arbitrary
* changeset as either the left or right part of a diff. This method allows
* the left and right halves of the displayed diff to be correctly mapped to
* storage changesets.
*
* @param id The Differential Changeset ID that comments added to the right
* side of the visible diff should be attached to.
* @param bool If true, attach new comments to the right side of the storage
* changeset. Note that this may be false, if the left side of
* some storage changeset is being shown as the right side of
* a display diff.
* @return this
*/
2011-02-04 00:41:58 +01:00
public function setRightSideCommentMapping($id, $is_new) {
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
$this->rightSideChangesetID = $id;
$this->rightSideAttachesToNewFile = $is_new;
return $this;
2011-02-04 00:41:58 +01:00
}
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
/**
* See setRightSideCommentMapping(), but this sets information for the left
* side of the display diff.
*/
2011-02-04 00:41:58 +01:00
public function setLeftSideCommentMapping($id, $is_new) {
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
$this->leftSideChangesetID = $id;
$this->leftSideAttachesToNewFile = $is_new;
return $this;
2011-02-04 00:41:58 +01:00
}
2011-01-25 00:52:35 +01:00
/**
* Set a key for identifying this changeset in the render cache. If set, the
* parser will attempt to use the changeset render cache, which can improve
* performance for frequently-viewed changesets.
*
* By default, there is no render cache key and parsers do not use the cache.
* This is appropriate for rarely-viewed changesets.
*
* NOTE: Currently, this key must be a valid Differential Changeset ID.
*
* @param string Key for identifying this changeset in the render cache.
* @return this
*/
public function setRenderCacheKey($key) {
$this->renderCacheKey = $key;
return $this;
}
private function getRenderCacheKey() {
return $this->renderCacheKey;
}
2011-01-25 00:52:35 +01:00
public function setChangeset($changeset) {
$this->changeset = $changeset;
$this->setFilename($changeset->getFilename());
$this->setChangesetID($changeset->getID());
return $this;
}
public function setWhitespaceMode($whitespace_mode) {
$this->whitespaceMode = $whitespace_mode;
return $this;
}
public function setChangesetID($changeset_id) {
$this->changesetID = $changeset_id;
return $this;
}
2011-03-31 04:22:11 +02:00
public function getChangeset() {
return $this->changeset;
}
2011-01-25 00:52:35 +01:00
public function getChangesetID() {
return $this->changesetID;
}
public function setFilename($filename) {
$this->filename = $filename;
if (strpos($filename, '.', 1) !== false) {
2011-02-03 07:38:42 +01:00
$parts = explode('.', $filename);
$this->filetype = end($parts);
2011-01-25 00:52:35 +01:00
}
}
2011-02-02 22:48:52 +01:00
2011-02-02 01:42:36 +01:00
public function setHandles(array $handles) {
$this->handles = $handles;
return $this;
}
2011-02-02 22:48:52 +01:00
2011-02-02 01:42:36 +01:00
public function setMarkupEngine(PhutilMarkupEngine $engine) {
$this->markupEngine = $engine;
return $this;
}
2011-01-25 00:52:35 +01:00
2011-02-02 19:10:25 +01:00
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
2011-01-25 00:52:35 +01:00
public function parseHunk(DifferentialHunk $hunk) {
$this->parsedHunk = true;
$lines = $hunk->getChanges();
// Flatten UTF-8 into "\0". We don't support UTF-8 because the diffing
// algorithms are byte-oriented (not character oriented) and everyone seems
// to be in agreement that it's fairly reasonable not to allow UTF-8 in
// source files. These bytes will later be replaced with a "?" glyph, but
// in the meantime we replace them with "\0" since Pygments is happy to
// deal with that.
$lines = preg_replace('/[\x80-\xFF]/', "\0", $lines);
$lines = str_replace(
array("\t", "\r\n", "\r"),
array(' ', "\n", "\n"),
$lines);
$lines = explode("\n", $lines);
$types = array();
foreach ($lines as $line_index => $line) {
if (isset($line[0])) {
$char = $line[0];
if ($char == ' ') {
$types[$line_index] = null;
} else if ($char == '\\' && $line_index > 0) {
$types[$line_index] = $types[$line_index - 1];
} else {
$types[$line_index] = $char;
}
} else {
$types[$line_index] = null;
}
}
$old_line = $hunk->getOldOffset();
$new_line = $hunk->getNewOffset();
$num_lines = count($lines);
if ($old_line > 1) {
$this->missingOld[$old_line] = true;
} else if ($new_line > 1) {
$this->missingNew[$new_line] = true;
}
for ($cursor = 0; $cursor < $num_lines; $cursor++) {
$type = $types[$cursor];
$data = array(
'type' => $type,
'text' => (string)substr($lines[$cursor], 1),
'line' => $new_line,
);
switch ($type) {
case '+':
$this->new[] = $data;
++$new_line;
break;
case '-':
$data['line'] = $old_line;
$this->old[] = $data;
++$old_line;
break;
default:
$this->new[] = $data;
$data['line'] = $old_line;
$this->old[] = $data;
++$new_line;
++$old_line;
break;
}
}
}
public function getDisplayLine($offset, $length) {
$start = 1;
for ($ii = $offset; $ii > 0; $ii--) {
if ($this->new[$ii] && $this->new[$ii]['line']) {
$start = $this->new[$ii]['line'];
break;
}
}
$end = $start;
for ($ii = $offset + $length; $ii < count($this->new); $ii++) {
if ($this->new[$ii] && $this->new[$ii]['line']) {
$end = $this->new[$ii]['line'];
break;
}
}
return "{$start},{$end}";
}
public function parseInlineComment(DifferentialInlineComment $comment) {
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
// Parse only comments which are actually visible.
if ($this->isCommentVisibleOnRenderedDiff($comment)) {
$this->comments[] = $comment;
}
2011-01-25 00:52:35 +01:00
return $this;
}
public function process() {
$old = array();
$new = array();
$n = 0;
$this->old = array_reverse($this->old);
$this->new = array_reverse($this->new);
$whitelines = false;
$changed = false;
$skip_intra = array();
while (count($this->old) || count($this->new)) {
$o_desc = array_pop($this->old);
$n_desc = array_pop($this->new);
$oend = end($this->old);
if ($oend) {
$o_next = $oend['type'];
} else {
$o_next = null;
}
$nend = end($this->new);
if ($nend) {
$n_next = $nend['type'];
} else {
$n_next = null;
}
if ($o_desc) {
$o_type = $o_desc['type'];
} else {
$o_type = null;
}
if ($n_desc) {
$n_type = $n_desc['type'];
} else {
$n_type = null;
}
if (($o_type != null) && ($n_type == null)) {
$old[] = $o_desc;
$new[] = null;
if ($n_desc) {
array_push($this->new, $n_desc);
}
$changed = true;
continue;
}
if (($n_type != null) && ($o_type == null)) {
$old[] = null;
$new[] = $n_desc;
if ($o_desc) {
array_push($this->old, $o_desc);
}
$changed = true;
continue;
}
if ($this->whitespaceMode != self::WHITESPACE_SHOW_ALL) {
$similar = false;
switch ($this->whitespaceMode) {
case self::WHITESPACE_IGNORE_TRAILING:
if (rtrim($o_desc['text']) == rtrim($n_desc['text'])) {
$similar = true;
}
break;
}
if ($similar) {
$o_desc['type'] = null;
$n_desc['type'] = null;
$skip_intra[count($old)] = true;
$whitelines = true;
} else {
$changed = true;
}
} else {
$changed = true;
}
$old[] = $o_desc;
$new[] = $n_desc;
}
$this->old = $old;
$this->new = $new;
if ($this->subparser) {
2011-01-25 00:52:35 +01:00
// Use this parser's side-by-side line information -- notably, the
// change types -- but replace all the line text with the subparser's.
// This lets us render whitespace-only changes without marking them as
// different.
2011-01-25 00:52:35 +01:00
$old = $this->old;
$new = $this->new;
$old_text = ipull($this->subparser->old, 'text', 'line');
$new_text = ipull($this->subparser->new, 'text', 'line');
2011-01-25 00:52:35 +01:00
foreach ($old as $k => $desc) {
if (empty($desc)) {
continue;
}
$old[$k]['text'] = idx($old_text, $desc['line']);
}
foreach ($new as $k => $desc) {
if (empty($desc)) {
continue;
}
$new[$k]['text'] = idx($new_text, $desc['line']);
}
$this->old = $old;
$this->new = $new;
}
$min_length = min(count($this->old), count($this->new));
for ($ii = 0; $ii < $min_length; $ii++) {
if ($this->old[$ii] || $this->new[$ii]) {
if (isset($this->old[$ii]['text'])) {
$otext = $this->old[$ii]['text'];
} else {
$otext = '';
}
if (isset($this->new[$ii]['text'])) {
$ntext = $this->new[$ii]['text'];
} else {
$ntext = '';
}
2011-01-25 00:52:35 +01:00
if ($otext != $ntext && empty($skip_intra[$ii])) {
$this->intra[$ii] = ArcanistDiffUtils::generateIntralineDiff(
$otext,
$ntext);
}
}
}
$lines_context = self::LINES_CONTEXT;
$max_length = max(count($this->old), count($this->new));
$old = $this->old;
$new = $this->new;
$visible = false;
$last = 0;
for ($cursor = -$lines_context; $cursor < $max_length; $cursor++) {
$offset = $cursor + $lines_context;
if ((isset($old[$offset]) && $old[$offset]['type']) ||
(isset($new[$offset]) && $new[$offset]['type'])) {
$visible = true;
$last = $offset;
} else if ($cursor > $last + $lines_context) {
$visible = false;
}
if ($visible && $cursor > 0) {
$this->visible[$cursor] = 1;
}
}
$old_corpus = ipull($this->old, 'text');
$old_corpus_block = implode("\n", $old_corpus);
$new_corpus = ipull($this->new, 'text');
$new_corpus_block = implode("\n", $new_corpus);
if ($this->noHighlight) {
$this->oldRender = explode("\n", phutil_escape_html($old_corpus_block));
$this->newRender = explode("\n", phutil_escape_html($new_corpus_block));
} else {
$this->oldRender = $this->sourceHighlight($this->old, $old_corpus_block);
$this->newRender = $this->sourceHighlight($this->new, $new_corpus_block);
}
$this->applyIntraline(
$this->oldRender,
ipull($this->intra, 0),
$old_corpus);
$this->applyIntraline(
$this->newRender,
ipull($this->intra, 1),
$new_corpus);
$this->tokenHighlight($this->oldRender);
$this->tokenHighlight($this->newRender);
$unchanged = false;
if ($this->subparser) {
2011-01-25 00:52:35 +01:00
$unchanged = $this->subparser->isUnchanged();
$whitelines = $this->subparser->isWhitespaceOnly();
} else if (!$changed) {
$filetype = $this->changeset->getFileType();
if ($filetype == DifferentialChangeType::FILE_TEXT ||
$filetype == DifferentialChangeType::FILE_SYMLINK) {
$unchanged = true;
}
}
$generated = (strpos($new_corpus_block, '@'.'generated') !== false);
$this->specialAttributes = array(
self::ATTR_GENERATED => $generated,
self::ATTR_UNCHANGED => $unchanged,
self::ATTR_DELETED => array_filter($this->old) &&
!array_filter($this->new),
self::ATTR_WHITELINES => $whitelines
);
}
public function loadCache() {
$render_cache_key = $this->getRenderCacheKey();
if (!$render_cache_key) {
2011-01-25 00:52:35 +01:00
return false;
}
$data = null;
2011-01-26 01:10:36 +01:00
$changeset = new DifferentialChangeset();
$conn_r = $changeset->establishConnection('r');
2011-01-25 00:52:35 +01:00
$data = queryfx_one(
2011-01-26 01:10:36 +01:00
$conn_r,
'SELECT * FROM %T WHERE id = %d',
$changeset->getTableName().'_parse_cache',
$render_cache_key);
2011-01-25 00:52:35 +01:00
if (!$data) {
return false;
}
$data = json_decode($data['cache'], true);
if (!is_array($data) || !$data) {
return false;
}
foreach (self::getCacheableProperties() as $cache_key) {
if (!array_key_exists($cache_key, $data)) {
// If we're missing a cache key, assume we're looking at an old cache
// and ignore it.
return false;
}
}
if ($data['cacheVersion'] !== self::CACHE_VERSION) {
return false;
}
unset($data['cacheVersion'], $data['cacheHost']);
$cache_prop = array_select_keys($data, self::getCacheableProperties());
foreach ($cache_prop as $cache_key => $v) {
$this->$cache_key = $v;
}
return true;
}
protected static function getCacheableProperties() {
return array(
'visible',
'new',
'old',
'intra',
'newRender',
'oldRender',
'specialAttributes',
'missingOld',
'missingNew',
'cacheVersion',
'cacheHost',
);
}
public function saveCache() {
$render_cache_key = $this->getRenderCacheKey();
if (!$render_cache_key) {
2011-01-25 00:52:35 +01:00
return false;
}
$cache = array();
foreach (self::getCacheableProperties() as $cache_key) {
switch ($cache_key) {
case 'cacheVersion':
$cache[$cache_key] = self::CACHE_VERSION;
break;
case 'cacheHost':
$cache[$cache_key] = php_uname('n');
break;
default:
$cache[$cache_key] = $this->$cache_key;
break;
}
}
$cache = json_encode($cache);
try {
2011-01-26 01:10:36 +01:00
$changeset = new DifferentialChangeset();
$conn_w = $changeset->establishConnection('w');
2011-01-25 00:52:35 +01:00
queryfx(
2011-01-26 01:10:36 +01:00
$conn_w,
'INSERT INTO %T (id, cache) VALUES (%d, %s)
2011-01-25 00:52:35 +01:00
ON DUPLICATE KEY UPDATE cache = VALUES(cache)',
2011-01-26 01:10:36 +01:00
$changeset->getTableName().'_parse_cache',
$render_cache_key,
2011-01-25 00:52:35 +01:00
$cache);
} catch (AphrontQueryException $ex) {
2011-01-25 00:52:35 +01:00
// TODO: uhoh
}
}
public function isGenerated() {
return idx($this->specialAttributes, self::ATTR_GENERATED, false);
}
public function isDeleted() {
return idx($this->specialAttributes, self::ATTR_DELETED, false);
}
public function isUnchanged() {
return idx($this->specialAttributes, self::ATTR_UNCHANGED, false);
}
public function isWhitespaceOnly() {
return idx($this->specialAttributes, self::ATTR_WHITELINES, false);
}
public function getLength() {
return max(count($this->old), count($this->new));
}
protected function applyIntraline(&$render, $intra, $corpus) {
foreach ($render as $key => $text) {
if (isset($intra[$key])) {
$render[$key] = ArcanistDiffUtils::applyIntralineDiff(
$text,
$intra[$key]);
}
if (isset($corpus[$key]) && strlen($corpus[$key]) > 80) {
$render[$key] = $this->lineWrap($render[$key]);
}
}
}
protected function lineWrap($l) {
$c = 0;
$len = strlen($l);
$ins = array();
for ($ii = 0; $ii < $len; ++$ii) {
if ($l[$ii] == '&') {
do {
++$ii;
} while ($l[$ii] != ';');
++$c;
} else if ($l[$ii] == '<') {
do {
++$ii;
} while ($l[$ii] != '>');
} else {
++$c;
}
if ($c == 80) {
$ins[] = ($ii + 1);
$c = 0;
}
}
while (($pos = array_pop($ins))) {
$l = substr_replace(
$l,
"<span class=\"over-the-line\">\xE2\xAC\x85</span><br />",
$pos,
0);
}
return $l;
}
protected function tokenHighlight(&$render) {
foreach ($render as $key => $text) {
$render[$key] = str_replace(
"\0",
'<span class="uu">'."\xEF\xBF\xBD".'</span>',
$text);
}
}
protected function sourceHighlight($data, $corpus) {
$result = $this->highlightEngine->highlightSource(
$this->filetype,
$corpus);
2011-01-26 01:10:36 +01:00
2011-01-25 00:52:35 +01:00
$result_lines = explode("\n", $result);
foreach ($data as $key => $info) {
if (!$info) {
unset($result_lines[$key]);
}
}
return $result_lines;
}
private function tryCacheStuff() {
$whitespace_mode = $this->whitespaceMode;
switch ($whitespace_mode) {
case self::WHITESPACE_SHOW_ALL:
case self::WHITESPACE_IGNORE_TRAILING:
break;
default:
$whitespace_mode = self::WHITESPACE_IGNORE_ALL;
break;
}
$skip_cache = ($whitespace_mode != self::WHITESPACE_IGNORE_ALL);
$this->whitespaceMode = $whitespace_mode;
$changeset = $this->changeset;
if ($changeset->getFileType() == DifferentialChangeType::FILE_TEXT ||
$changeset->getFileType() == DifferentialChangeType::FILE_SYMLINK) {
if ($skip_cache || !$this->loadCache()) {
if ($this->whitespaceMode == self::WHITESPACE_IGNORE_ALL) {
// Huge mess. Generate a "-bw" (ignore all whitespace changes) diff,
// parse it out, and then play a shell game with the parsed format
// in process() so we highlight only changed lines but render
// whitespace differences. If we don't do this, we either fail to
// render whitespace changes (which is incredibly confusing,
// especially for python) or often produce a much larger set of
// differences than necessary.
$old_tmp = new TempFile();
$new_tmp = new TempFile();
Filesystem::writeFile($old_tmp, $changeset->makeOldFile());
Filesystem::writeFile($new_tmp, $changeset->makeNewFile());
list($err, $diff) = exec_manual(
2011-02-03 22:20:43 +01:00
'diff -bw -U65535 %s %s',
2011-01-25 00:52:35 +01:00
$old_tmp,
$new_tmp);
if (!strlen($diff)) {
// If there's no diff text, that means the files are identical
// except for whitespace changes. Build a synthetic, changeless
// diff. TODO: this is incredibly hacky.
$entire_file = explode("\n", $changeset->makeOldFile());
foreach ($entire_file as $k => $line) {
$entire_file[$k] = ' '.$line;
}
$len = count($entire_file);
$entire_file = implode("\n", $entire_file);
$diff = <<<EOSYNTHETIC
--- ignored 9999-99-99
+++ ignored 9999-99-99
@@ -{$len},{$len} +{$len},{$len} @@
{$entire_file}
EOSYNTHETIC;
}
// subparser takes over the current non-whitespace-ignoring changeset
$this->subparser = new DifferentialChangesetParser();
foreach ($changeset->getHunks() as $hunk) {
$this->subparser->parseHunk($hunk);
}
2011-01-25 00:52:35 +01:00
// this parser takes new changeset; will use subparser's text later
$changes = id(new ArcanistDiffParser())->parseDiff($diff);
2011-01-25 00:52:35 +01:00
$diff = DifferentialDiff::newFromRawChanges($changes);
2011-02-03 22:20:43 +01:00
$changesets = $diff->getChangesets();
$changeset = reset($changesets);
$this->setChangeset($changeset);
2011-01-25 00:52:35 +01:00
}
foreach ($changeset->getHunks() as $hunk) {
$this->parseHunk($hunk);
}
$this->process();
if (!$skip_cache) {
$this->saveCache();
}
}
}
}
public function render(
$range_start = null,
$range_len = null,
$mask_force = array()) {
$this->highlightEngine = new PhutilDefaultSyntaxHighlighterEngine();
$this->highlightEngine->setConfig(
'pygments.enabled',
PhabricatorEnv::getEnvConfig('pygments.enabled'));
2011-01-25 00:52:35 +01:00
$this->tryCacheStuff();
$feedback_mask = array();
switch ($this->changeset->getFileType()) {
case DifferentialChangeType::FILE_IMAGE:
$old = null;
$cur = null;
$metadata = $this->changeset->getMetadata();
$data = idx($metadata, 'attachment-data');
$old_phid = idx($metadata, 'old:binary-phid');
$new_phid = idx($metadata, 'new:binary-phid');
if ($old_phid || $new_phid) {
if ($old_phid) {
$old_uri = PhabricatorFileURI::getViewURIForPHID($old_phid);
$old = phutil_render_tag(
'img',
array(
'src' => $old_uri,
));
}
if ($new_phid) {
$new_uri = PhabricatorFileURI::getViewURIForPHID($new_phid);
$cur = phutil_render_tag(
'img',
array(
'src' => $new_uri,
));
}
}
$output = $this->renderChangesetTable(
$this->changeset,
'<tr>'.
'<th></th>'.
'<td class="differential-old-image">'.
'<div class="differential-image-stage">'.
$old.
'</div>'.
'</td>'.
'<th></th>'.
'<td class="differential-new-image">'.
'<div class="differential-image-stage">'.
$cur.
'</div>'.
'</td>'.
'</tr>');
return $output;
case DifferentialChangeType::FILE_DIRECTORY:
case DifferentialChangeType::FILE_BINARY:
$output = $this->renderChangesetTable($this->changeset, null);
return $output;
}
$shield = null;
if ($range_start === null && $range_len === null) {
if ($this->isGenerated()) {
$shield = $this->renderShield(
"This file contains generated code, which does not normally need ".
"to be reviewed.",
true);
} else if ($this->isUnchanged()) {
if ($this->isWhitespaceOnly()) {
$shield = $this->renderShield(
"This file was changed only by adding or removing trailing ".
"whitespace.",
false);
} else {
$shield = $this->renderShield(
"The contents of this file were not changed.",
false);
}
} else if ($this->isDeleted()) {
$shield = $this->renderShield(
"This file was completely deleted.",
true);
} else if ($this->changeset->getAffectedLineCount() > 2500) {
$lines = number_format($this->changeset->getAffectedLineCount());
$shield = $this->renderShield(
"This file has a very large number of changes ({$lines} lines).",
true);
} else if (preg_match('/\.sql3$/', $this->changeset->getFilename())) {
$shield = $this->renderShield(
".sql3 files are hidden by default.",
true);
}
}
if ($shield) {
return $this->renderChangesetTable($this->changeset, $shield);
}
$old_comments = array();
$new_comments = array();
$old_mask = array();
$new_mask = array();
$feedback_mask = array();
if ($this->comments) {
foreach ($this->comments as $comment) {
$start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0);
2011-02-02 01:42:36 +01:00
$end = $comment->getLineNumber() +
$comment->getLineLength() +
self::LINES_CONTEXT;
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
$new = $this->isCommentOnRightSideWhenDisplayed($comment);
2011-01-25 00:52:35 +01:00
for ($ii = $start; $ii <= $end; $ii++) {
if ($new) {
$new_mask[$ii] = true;
} else {
$old_mask[$ii] = true;
}
}
}
foreach ($this->old as $ii => $old) {
if (isset($old['line']) && isset($old_mask[$old['line']])) {
$feedback_mask[$ii] = true;
}
}
foreach ($this->new as $ii => $new) {
if (isset($new['line']) && isset($new_mask[$new['line']])) {
$feedback_mask[$ii] = true;
}
}
$this->comments = msort($this->comments, 'getID');
foreach ($this->comments as $comment) {
2011-02-02 01:42:36 +01:00
$final = $comment->getLineNumber() +
$comment->getLineLength();
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
2011-02-02 01:42:36 +01:00
$new_comments[$final][] = $comment;
2011-01-25 00:52:35 +01:00
} else {
2011-02-02 01:42:36 +01:00
$old_comments[$final][] = $comment;
2011-01-25 00:52:35 +01:00
}
}
}
$html = $this->renderTextChange(
$range_start,
$range_len,
$mask_force,
$feedback_mask,
$old_comments,
$new_comments);
return $this->renderChangesetTable($this->changeset, $html);
}
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
/**
* Determine if an inline comment will appear on the rendered diff,
* taking into consideration which halves of which changesets will actually
* be shown.
*
* @param DifferentialInlineComment Comment to test for visibility.
* @return bool True if the comment is visible on the rendered diff.
*/
private function isCommentVisibleOnRenderedDiff(
DifferentialInlineComment $comment) {
$changeset_id = $comment->getChangesetID();
$is_new = $comment->getIsNewFile();
if ($changeset_id == $this->rightSideChangesetID &&
$is_new == $this->rightSideAttachesToNewFile) {
return true;
}
if ($changeset_id == $this->leftSideChangesetID &&
$is_new == $this->leftSideAttachesToNewFile) {
return true;
}
return false;
}
/**
* Determine if a comment will appear on the right side of the display diff.
* Note that the comment must appear somewhere on the rendered changeset, as
* per isCommentVisibleOnRenderedDiff().
*
* @param DifferentialInlineComment Comment to test for display location.
* @return bool True for right, false for left.
*/
private function isCommentOnRightSideWhenDisplayed(
DifferentialInlineComment $comment) {
if (!$this->isCommentVisibleOnRenderedDiff($comment)) {
throw new Exception("Comment is not visible on changeset!");
}
$changeset_id = $comment->getChangesetID();
$is_new = $comment->getIsNewFile();
if ($changeset_id == $this->rightSideChangesetID &&
$is_new == $this->rightSideAttachesToNewFile) {
return true;
2011-01-25 00:52:35 +01:00
}
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
return false;
2011-01-25 00:52:35 +01:00
}
protected function renderShield($message, $more) {
if ($more) {
2011-03-31 04:22:11 +02:00
$end = $this->getLength();
$reference = $this->getChangeset()->getRenderingReference();
2011-01-25 00:52:35 +01:00
$more =
' '.
javelin_render_tag(
2011-01-25 00:52:35 +01:00
'a',
array(
'mustcapture' => true,
'sigil' => 'show-more',
'class' => 'complete',
'href' => '#',
'meta' => array(
2011-03-31 04:22:11 +02:00
'id' => $reference,
'range' => "0-{$end}",
),
2011-01-25 00:52:35 +01:00
),
'Show File Contents');
} else {
$more = null;
}
return javelin_render_tag(
2011-01-25 00:52:35 +01:00
'tr',
array(
'sigil' => 'context-target',
2011-01-25 00:52:35 +01:00
),
'<td class="differential-shield" colspan="4">'.
phutil_escape_html($message).
$more.
'</td>');
}
protected function renderTextChange(
$range_start,
$range_len,
$mask_force,
$feedback_mask,
array $old_comments,
array $new_comments) {
$context_not_available = null;
if ($this->missingOld || $this->missingNew) {
$context_not_available = javelin_render_tag(
2011-01-25 00:52:35 +01:00
'tr',
array(
'sigil' => 'context-target',
),
'<td colspan="4" class="show-more">'.
'Context not available.'.
'</td>');
$context_not_available = $context_not_available;
}
$html = array();
$rows = max(
count($this->old),
count($this->new));
if ($range_start === null) {
$range_start = 0;
}
if ($range_len === null) {
$range_len = $rows;
}
$range_len = min($range_len, $rows - $range_start);
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
// Gaps - compute gaps in the visible display diff, where we will render
// "Show more context" spacers. This builds an aggregate $mask of all the
// lines we must show (because they are near changed lines, near inline
// comments, or the request has explicitly asked for them, i.e. resulting
// from the user clicking "show more") and then finds all the gaps between
// visible lines. If a gap is smaller than the context size, we just
// display it. Otherwise, we record it into $gaps and will render a
// "show more context" element instead of diff text below.
2011-01-25 00:52:35 +01:00
$gaps = array();
$gap_start = 0;
$in_gap = false;
$mask = $this->visible + $mask_force + $feedback_mask;
$mask[$range_start + $range_len] = true;
for ($ii = $range_start; $ii <= $range_start + $range_len; $ii++) {
if (isset($mask[$ii])) {
if ($in_gap) {
$gap_length = $ii - $gap_start;
if ($gap_length <= self::LINES_CONTEXT) {
for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) {
$mask[$jj] = true;
}
} else {
$gaps[] = array($gap_start, $gap_length);
}
$in_gap = false;
}
} else {
if (!$in_gap) {
$gap_start = $ii;
$in_gap = true;
}
}
}
$gaps = array_reverse($gaps);
2011-03-31 04:22:11 +02:00
$reference = $this->getChangeset()->getRenderingReference();
2011-01-25 00:52:35 +01:00
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
$left_id = $this->leftSideChangesetID;
$right_id = $this->rightSideChangesetID;
// "N" stands for 'new' and means the comment should attach to the new file
// when stored, i.e. DifferentialInlineComment->setIsNewFile().
// "O" stands for 'old' and means the comment should attach to the old file.
$left_char = $this->leftSideAttachesToNewFile
? 'N'
: 'O';
$right_char = $this->rightSideAttachesToNewFile
? 'N'
: 'O';
2011-01-25 00:52:35 +01:00
for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
if (empty($mask[$ii])) {
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
// If we aren't going to show this line, we've just entered a gap.
// Pop information about the next gap off the $gaps stack and render
// an appropriate "Show more context" element. This branch eventually
// increments $ii by the entire size of the gap and then continues
// the loop.
2011-01-25 00:52:35 +01:00
$gap = array_pop($gaps);
$top = $gap[0];
$len = $gap[1];
$end = $top + $len - 20;
$contents = array();
if ($len > 40) {
$contents[] = javelin_render_tag(
2011-01-25 00:52:35 +01:00
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
2011-03-31 04:22:11 +02:00
'id' => $reference,
'range' => "{$top}-{$len}/{$top}-20",
),
2011-01-25 00:52:35 +01:00
),
"\xE2\x96\xB2 Show 20 Lines");
}
$contents[] = javelin_render_tag(
2011-01-25 00:52:35 +01:00
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
2011-03-31 04:22:11 +02:00
'id' => $reference,
'range' => "{$top}-{$len}/{$top}-{$len}",
),
2011-01-25 00:52:35 +01:00
),
'Show All '.$len.' Lines');
if ($len > 40) {
$contents[] = javelin_render_tag(
2011-01-25 00:52:35 +01:00
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
2011-03-31 04:22:11 +02:00
'id' => $reference,
'range' => "{$top}-{$len}/{$end}-20",
),
2011-01-25 00:52:35 +01:00
),
"\xE2\x96\xBC Show 20 Lines");
};
$container = javelin_render_tag(
2011-01-25 00:52:35 +01:00
'tr',
array(
'sigil' => 'context-target',
),
'<td colspan="4" class="show-more">'.
implode(' &bull; ', $contents).
'</td>');
$html[] = $container;
$ii += ($len - 1);
continue;
}
if (isset($this->old[$ii])) {
$o_num = $this->old[$ii]['line'];
$o_text = isset($this->oldRender[$ii]) ? $this->oldRender[$ii] : null;
2011-01-25 00:52:35 +01:00
$o_attr = null;
if ($this->old[$ii]['type']) {
if (empty($this->new[$ii])) {
$o_attr = ' class="old old-full"';
} else {
$o_attr = ' class="old"';
}
}
} else {
$o_num = null;
$o_text = null;
$o_attr = null;
}
if (isset($this->new[$ii])) {
$n_num = $this->new[$ii]['line'];
$n_text = isset($this->newRender[$ii]) ? $this->newRender[$ii] : null;
2011-01-25 00:52:35 +01:00
$n_attr = null;
if ($this->new[$ii]['type']) {
if (empty($this->old[$ii])) {
$n_attr = ' class="new new-full"';
} else {
$n_attr = ' class="new"';
}
}
} else {
$n_num = null;
$n_text = null;
$n_attr = null;
}
if (($o_num && !empty($this->missingOld[$o_num])) ||
($n_num && !empty($this->missingNew[$n_num]))) {
$html[] = $context_not_available;
}
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
if ($o_num && $left_id) {
$o_id = ' id="C'.$left_id.$left_char.'L'.$o_num.'"';
2011-01-25 00:52:35 +01:00
} else {
$o_id = null;
}
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
if ($n_num && $right_id) {
$n_id = ' id="C'.$right_id.$right_char.'L'.$n_num.'"';
2011-01-25 00:52:35 +01:00
} else {
$n_id = null;
}
// NOTE: The Javascript is sensitive to whitespace changes in this
// block!
$html[] =
'<tr>'.
'<th'.$o_id.'>'.$o_num.'</th>'.
'<td'.$o_attr.'>'.$o_text.'</td>'.
'<th'.$n_id.'>'.$n_num.'</th>'.
'<td'.$n_attr.'>'.$n_text.'</td>'.
'</tr>';
if ($context_not_available && ($ii == $rows - 1)) {
$html[] = $context_not_available;
}
if ($o_num && isset($old_comments[$o_num])) {
foreach ($old_comments[$o_num] as $comment) {
2011-02-02 19:10:25 +01:00
$xhp = $this->renderInlineComment($comment);
2011-01-25 00:52:35 +01:00
$html[] =
'<tr class="inline"><th /><td>'.
$xhp.
'</td><th /><td /></tr>';
}
}
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $comment) {
2011-02-02 19:10:25 +01:00
$xhp = $this->renderInlineComment($comment);
2011-01-25 00:52:35 +01:00
$html[] =
'<tr class="inline"><th /><td /><th /><td>'.
$xhp.
'</td></tr>';
}
}
}
return implode('', $html);
}
2011-02-02 19:10:25 +01:00
private function renderInlineComment(DifferentialInlineComment $comment) {
2011-01-25 00:52:35 +01:00
2011-02-02 19:10:25 +01:00
$user = $this->user;
$edit = $user &&
($comment->getAuthorPHID() == $user->getPHID()) &&
(!$comment->getCommentID());
2011-01-25 00:52:35 +01:00
Make DifferentialChangesetParser explicitly map display to storage for comments Summary: This is a followup to D228. Basically, we use "changeset" (and, implicitly, changesetID) for way too much stuff now. One thing it can't possibly capture is the complete, arbitrary mapping between the left and right sides of the displayed diff and the places we want to store the left and right side comments. This causes a bunch of bugs; basically adding inline comments is completely broken in diff-of-diff views prior to this patch. Make this mapping explicit. Note that the renderer already passes this mapping to DifferentialChangesetParser which is why there are no changes outside this file, I just didn't finish the implementation during the port. This has the nice side-effect of fixing T132 and several other bugs. Test Plan: Made new-file and old-file comments on a normal diff; reloaded page, verified comments didn't do anything crazy. Expanded text on a normal diff, made new-file and old-file comments; reloaded page, verified comments. Repeated these steps for a previous diff in the same revision; verified comments. Loaded diff-of-diffs and verified expected comments appeared. Made new left and right hand side comments, which almost work, see below. NOTE: There is still a bug where comments made in the left-display-side of a diff-of-diffs will incorrectly be written to the right-storage-side of the right-display-side diff. However, this is an issue with the JS (the PHP is correct) so I want to pull it out of scope for this patch since I think I need to fix some other JS stuff too and this improves the overall state of the world even if not everything is fixed. Reviewed By: tuomaspelkonen Reviewers: jungejason, tuomaspelkonen, aran, ola CC: aran, tuomaspelkonen, epriestley Differential Revision: 237
2011-05-06 16:18:09 +02:00
$on_right = $this->isCommentOnRightSideWhenDisplayed($comment);
2011-01-25 00:52:35 +01:00
2011-02-02 01:42:36 +01:00
return id(new DifferentialInlineCommentView())
->setInlineComment($comment)
2011-02-02 19:10:25 +01:00
->setOnRight($on_right)
2011-02-02 01:42:36 +01:00
->setHandles($this->handles)
->setMarkupEngine($this->markupEngine)
2011-02-02 19:10:25 +01:00
->setEditable($edit)
2011-02-02 01:42:36 +01:00
->render();
2011-01-25 00:52:35 +01:00
}
protected function renderPropertyChangeHeader($changeset) {
$old = $changeset->getOldProperties();
$new = $changeset->getNewProperties();
if ($old === $new) {
return null;
}
if ($changeset->getChangeType() == DifferentialChangeType::TYPE_ADD &&
$new == array('unix:filemode' => '100644')) {
return null;
}
if ($changeset->getChangeType() == DifferentialChangeType::TYPE_DELETE &&
$old == array('unix:filemode' => '100644')) {
return null;
}
return null;
/*
2011-02-02 01:42:36 +01:00
TODO
2011-01-25 00:52:35 +01:00
$table = <table class="differential-property-table" />;
$table->appendChild(
<tr class="property-table-header">
<th>Property Changes</th>
<td class="oval">Old Value</td>
<td class="nval">New Value</td>
</tr>);
$keys = array_keys($old + $new);
sort($keys);
foreach ($keys as $key) {
$oval = idx($old, $key);
$nval = idx($new, $key);
if ($oval !== $nval) {
if ($oval === null) {
$oval = <em>null</em>;
}
if ($nval === null) {
$nval = <em>null</em>;
}
$table->appendChild(
<tr>
<th>{$key}</th>
<td class="oval">{$oval}</td>
<td class="nval">{$nval}</td>
</tr>);
}
}
return $table;
*/
}
protected function renderChangesetTable($changeset, $contents) {
$props = $this->renderPropertyChangeHeader($this->changeset);
$table = null;
if ($contents) {
$table =
'<table class="differential-diff remarkup-code PhabricatorMonospaced">'.
2011-01-25 00:52:35 +01:00
$contents.
'</table>';
}
if (!$table && !$props) {
$notice = $this->renderChangeTypeHeader($this->changeset, true);
} else {
$notice = $this->renderChangeTypeHeader($this->changeset, false);
}
return implode(
"\n",
array(
$notice,
$props,
$table,
));
}
protected function renderChangeTypeHeader($changeset, $force) {
static $articles = array(
DifferentialChangeType::FILE_IMAGE => 'an',
);
static $files = array(
DifferentialChangeType::FILE_TEXT => 'file',
DifferentialChangeType::FILE_IMAGE => 'image',
DifferentialChangeType::FILE_DIRECTORY => 'directory',
DifferentialChangeType::FILE_BINARY => 'binary file',
DifferentialChangeType::FILE_SYMLINK => 'symlink',
);
static $changes = array(
DifferentialChangeType::TYPE_ADD => 'added',
DifferentialChangeType::TYPE_CHANGE => 'changed',
DifferentialChangeType::TYPE_DELETE => 'deleted',
DifferentialChangeType::TYPE_MOVE_HERE => 'moved from',
DifferentialChangeType::TYPE_COPY_HERE => 'copied from',
DifferentialChangeType::TYPE_MOVE_AWAY => 'moved to',
DifferentialChangeType::TYPE_COPY_AWAY => 'copied to',
DifferentialChangeType::TYPE_MULTICOPY
=> 'deleted after being copied to',
);
$change = $changeset->getChangeType();
$file = $changeset->getFileType();
$message = null;
if ($change == DifferentialChangeType::TYPE_CHANGE &&
$file == DifferentialChangeType::FILE_TEXT) {
if ($force) {
// We have to force something to render because there were no changes
// of other kinds.
$message = "This {$files[$file]} was not modified.";
} else {
// Default case of changes to a text file, no metadata.
return null;
}
} else {
$verb = idx($changes, $change, 'changed');
switch ($change) {
default:
$message = "This {$files[$file]} was <strong>{$verb}</strong>.";
break;
case DifferentialChangeType::TYPE_MOVE_HERE:
case DifferentialChangeType::TYPE_COPY_HERE:
$message =
"This {$files[$file]} was {$verb} ".
"<strong>{$changeset->getOldFile()}</strong>.";
break;
case DifferentialChangeType::TYPE_MOVE_AWAY:
case DifferentialChangeType::TYPE_COPY_AWAY:
case DifferentialChangeType::TYPE_MULTICOPY:
$paths = $changeset->getAwayPaths();
if (count($paths) > 1) {
$message =
"This {$files[$file]} was {$verb}: ".
"<strong>".implode(', ', $paths)."</strong>.";
} else {
$message =
"This {$files[$file]} was {$verb} ".
"<strong>".reset($paths)."</strong>.";
}
break;
case DifferentialChangeType::TYPE_CHANGE:
$message = "This is ".idx($articles, $file, 'a')." {$files[$file]}.";
break;
}
}
return
'<div class="differential-meta-notice">'.
$message.
'</div>';
}
public function renderForEmail() {
$ret = '';
$min = min(count($this->old), count($this->new));
for ($i = 0; $i < $min; $i++) {
$o = $this->old[$i];
$n = $this->new[$i];
if (!isset($this->visible[$i])) {
continue;
}
if ($o['line'] && $n['line']) {
// It is quite possible there are better ways to achieve this. For
// example, "white-space: pre;" can do a better job, WERE IT NOT for
// broken email clients like OWA which use newlines to do weird
// wrapping. So dont give them newlines.
if (isset($this->intra[$i])) {
$ret .= sprintf(
"<font color=\"red\">-&nbsp;%s</font><br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($o['text']))
);
$ret .= sprintf(
"<font color=\"green\">+&nbsp;%s</font><br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($n['text']))
);
} else {
$ret .= sprintf("&nbsp;&nbsp;%s<br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($n['text']))
);
}
} else if ($o['line'] && !$n['line']) {
$ret .= sprintf(
"<font color=\"red\">-&nbsp;%s</font><br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($o['text']))
);
} else {
$ret .= sprintf(
"<font color=\"green\">+&nbsp;%s</font><br/>",
str_replace(" ", "&nbsp;", phutil_escape_html($n['text']))
);
}
}
return $ret;
}
}