2011-01-25 00:52:35 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
2012-01-06 05:29:16 +01:00
|
|
|
* Copyright 2012 Facebook, Inc.
|
2011-01-25 00:52:35 +01:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2012-03-13 19:18:11 +01:00
|
|
|
final class DifferentialChangesetParser {
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
protected $visible = array();
|
|
|
|
protected $new = array();
|
|
|
|
protected $old = array();
|
|
|
|
protected $intra = array();
|
|
|
|
protected $newRender = null;
|
|
|
|
protected $oldRender = null;
|
|
|
|
|
|
|
|
protected $filename = null;
|
|
|
|
protected $missingOld = array();
|
|
|
|
protected $missingNew = array();
|
|
|
|
|
|
|
|
protected $comments = array();
|
|
|
|
protected $specialAttributes = array();
|
|
|
|
|
|
|
|
protected $changeset;
|
|
|
|
protected $whitespaceMode = null;
|
|
|
|
|
|
|
|
protected $subparser;
|
2011-02-02 22:48:52 +01:00
|
|
|
|
2011-05-05 16:08:10 +02: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
|
|
|
|
2011-05-09 06:22:25 +02:00
|
|
|
private $leftSideChangesetID;
|
|
|
|
private $leftSideAttachesToNewFile;
|
|
|
|
|
|
|
|
private $rightSideChangesetID;
|
|
|
|
private $rightSideAttachesToNewFile;
|
|
|
|
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
private $renderingReference;
|
2011-05-21 05:40:00 +02:00
|
|
|
private $isSubparser;
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
|
2011-06-08 21:39:03 +02:00
|
|
|
private $lineWidth = 80;
|
2011-06-20 22:49:17 +02:00
|
|
|
private $isTopLevel;
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
private $coverage;
|
2012-03-20 03:17:59 +01:00
|
|
|
private $markupEngine;
|
2012-04-13 08:52:50 +02:00
|
|
|
private $highlightErrors;
|
2011-06-08 21:39:03 +02:00
|
|
|
|
2012-04-13 08:52:50 +02:00
|
|
|
const CACHE_VERSION = 5;
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
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';
|
2012-03-13 01:06:36 +01:00
|
|
|
|
|
|
|
// TODO: This is now "Ignore Most" in the UI.
|
2011-01-25 00:52:35 +01:00
|
|
|
const WHITESPACE_IGNORE_ALL = 'ignore-all';
|
|
|
|
|
2012-03-13 01:06:36 +01:00
|
|
|
const WHITESPACE_IGNORE_FORCE = 'ignore-force';
|
|
|
|
|
2011-05-09 06:22:25 +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) {
|
2011-05-09 06:22:25 +02:00
|
|
|
$this->rightSideChangesetID = $id;
|
|
|
|
$this->rightSideAttachesToNewFile = $is_new;
|
|
|
|
return $this;
|
2011-02-04 00:41:58 +01:00
|
|
|
}
|
|
|
|
|
2011-05-09 06:22:25 +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) {
|
2011-05-09 06:22:25 +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
|
|
|
|
2011-05-05 16:08:10 +02: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;
|
|
|
|
}
|
|
|
|
|
2011-06-08 21:39:03 +02:00
|
|
|
/**
|
|
|
|
* Set the character width at which lines will be wrapped. Defaults to 80.
|
|
|
|
*
|
|
|
|
* @param int Hard-wrap line-width for diff display.
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public function setLineWidth($width) {
|
|
|
|
$this->lineWidth = $width;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-05-05 16:08:10 +02:00
|
|
|
private function getRenderCacheKey() {
|
|
|
|
return $this->renderCacheKey;
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
public function setChangeset($changeset) {
|
|
|
|
$this->changeset = $changeset;
|
2011-06-08 21:39:03 +02:00
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
$this->setFilename($changeset->getFilename());
|
2011-06-08 21:39:03 +02:00
|
|
|
$this->setLineWidth($changeset->getWordWrapWidth());
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setWhitespaceMode($whitespace_mode) {
|
|
|
|
$this->whitespaceMode = $whitespace_mode;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
public function setRenderingReference($ref) {
|
|
|
|
$this->renderingReference = $ref;
|
2011-01-25 00:52:35 +01:00
|
|
|
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 setFilename($filename) {
|
|
|
|
$this->filename = $filename;
|
2011-07-06 20:10:40 +02:00
|
|
|
return $this;
|
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) {
|
2012-04-04 22:13:08 +02:00
|
|
|
assert_instances_of($handles, 'PhabricatorObjectHandle');
|
2011-02-02 01:42:36 +01:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
public function setCoverage($coverage) {
|
|
|
|
$this->coverage = $coverage;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
public function parseHunk(DifferentialHunk $hunk) {
|
|
|
|
$lines = $hunk->getChanges();
|
|
|
|
|
|
|
|
$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 {
|
|
|
|
$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,
|
|
|
|
);
|
2012-03-22 22:13:48 +01:00
|
|
|
if ($type == '\\') {
|
2012-03-15 00:45:40 +01:00
|
|
|
$type = $types[$cursor - 1];
|
|
|
|
$data['text'] = ltrim($data['text']);
|
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add inline comments to Diffusion/Audit
Summary:
- Add inline comments to Audits, like Differential.
- Creates new storage for the comments in the Audits database.
- Creates a new PhabricatorAuditInlineComment class, similar to DifferentialInlineComment.
- Defines an Interface which Differential and Audit comments conform to.
- Makes consumers of DifferentialInlineComments consume objects which implement that interface instead.
- Adds save
NOTE: Some features are still missing! Wanted to cut this off before it got crazy:
- Inline comments aren't shown in the main comment list.
- Inline comments aren't shown in the emails.
- Inline comments aren't previewed.
I'll followup with those but this was getting pretty big.
@vrana, does the SQL change look correct?
Test Plan:
- Created, edited, deleted, replied to, reloaded and saved inline comments in Diffusion, on the left and right side of diffs.
- Created, edited, deleted, replied to, reloaded and saved inline comments in Differentila, on the left and right side of primary and diff-versus-diff diffs.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T904
Differential Revision: https://secure.phabricator.com/D1898
2012-03-14 20:56:01 +01:00
|
|
|
public function parseInlineComment(
|
|
|
|
PhabricatorInlineCommentInterface $comment) {
|
|
|
|
|
2011-05-09 06:22:25 +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);
|
|
|
|
|
|
|
|
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'])) {
|
2011-06-29 01:21:06 +02:00
|
|
|
if ($o_desc['type']) {
|
|
|
|
// If we're converting this into an unchanged line because of
|
|
|
|
// a trailing whitespace difference, mark it as a whitespace
|
|
|
|
// change so we can show "This file was modified only by
|
|
|
|
// adding or removing trailing whitespace." instead of
|
|
|
|
// "This file was not modified.".
|
|
|
|
$whitelines = true;
|
|
|
|
}
|
|
|
|
$similar = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// In this case, the lines are similar if there is no change type
|
|
|
|
// (that is, just trust the diff algorithm).
|
|
|
|
if (!$o_desc['type']) {
|
2011-01-25 00:52:35 +01:00
|
|
|
$similar = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ($similar) {
|
2012-03-22 22:13:48 +01:00
|
|
|
if ($o_desc['type'] == '\\') {
|
|
|
|
// These are similar because they're "No newline at end of file"
|
|
|
|
// comments.
|
|
|
|
} else {
|
|
|
|
$o_desc['type'] = null;
|
|
|
|
$n_desc['type'] = null;
|
|
|
|
$skip_intra[count($old)] = true;
|
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
} else {
|
|
|
|
$changed = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$old[] = $o_desc;
|
|
|
|
$new[] = $n_desc;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->old = $old;
|
|
|
|
$this->new = $new;
|
2011-05-21 05:40:00 +02:00
|
|
|
|
|
|
|
$unchanged = false;
|
|
|
|
if ($this->subparser) {
|
|
|
|
$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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->specialAttributes = array(
|
|
|
|
self::ATTR_UNCHANGED => $unchanged,
|
|
|
|
self::ATTR_DELETED => array_filter($this->old) &&
|
|
|
|
!array_filter($this->new),
|
|
|
|
self::ATTR_WHITELINES => $whitelines
|
|
|
|
);
|
|
|
|
|
|
|
|
if ($this->isSubparser) {
|
|
|
|
// The rest of this function deals with formatting the diff for display;
|
|
|
|
// we can exit early if we're a subparser and avoid doing extra work.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-04-28 23:32:29 +02:00
|
|
|
if ($this->subparser) {
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2011-04-28 23:32:29 +02: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
|
|
|
|
2011-04-28 23:32:29 +02: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']);
|
2011-06-07 20:02:11 +02:00
|
|
|
|
2012-03-13 01:06:36 +01:00
|
|
|
if ($this->whitespaceMode == self::WHITESPACE_IGNORE_FORCE) {
|
|
|
|
// Under forced ignore mode, ignore even internal whitespace
|
|
|
|
// changes.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-06-07 20:02:11 +02:00
|
|
|
// If there's a corresponding "old" text and the line is marked as
|
|
|
|
// unchanged, test if there are internal whitespace changes between
|
|
|
|
// non-whitespace characters, e.g. spaces added to a string or spaces
|
|
|
|
// added around operators. If we find internal spaces, mark the line
|
|
|
|
// as changed.
|
|
|
|
//
|
|
|
|
// We only need to do this for "new" lines because any line that is
|
|
|
|
// missing either "old" or "new" text certainly can not have internal
|
|
|
|
// whitespace changes without also having non-whitespace changes,
|
|
|
|
// because characters had to be either added or removed to create the
|
|
|
|
// possibility of internal whitespace.
|
|
|
|
if (isset($old[$k]['text']) && empty($new[$k]['type'])) {
|
|
|
|
if (trim($old[$k]['text']) != trim($new[$k]['text'])) {
|
|
|
|
// The strings aren't the same when trimmed, so there are internal
|
|
|
|
// whitespace changes. Mark this line changed.
|
|
|
|
$old[$k]['type'] = '-';
|
|
|
|
$new[$k]['type'] = '+';
|
2012-03-13 02:20:51 +01:00
|
|
|
|
|
|
|
// Re-mark this line for intraline diffing.
|
|
|
|
unset($skip_intra[$k]);
|
2011-06-07 20:02:11 +02:00
|
|
|
}
|
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$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]) {
|
2011-01-25 02:39:14 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-17 20:06:02 +02:00
|
|
|
$old_corpus = array();
|
|
|
|
foreach ($this->old as $o) {
|
2012-03-15 02:24:29 +01:00
|
|
|
if ($o['type'] != '\\') {
|
|
|
|
$old_corpus[] = $o['text'];
|
|
|
|
}
|
2011-07-17 20:06:02 +02:00
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
$old_corpus_block = implode("\n", $old_corpus);
|
|
|
|
|
2011-07-17 20:06:02 +02:00
|
|
|
$new_corpus = array();
|
|
|
|
foreach ($this->new as $n) {
|
2012-03-15 02:24:29 +01:00
|
|
|
if ($n['type'] != '\\') {
|
|
|
|
$new_corpus[] = $n['text'];
|
|
|
|
}
|
2011-07-17 20:06:02 +02:00
|
|
|
}
|
2012-04-18 01:23:29 +02:00
|
|
|
$new_corpus_block = implode("\n", $new_corpus);
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2012-05-01 07:31:26 +02:00
|
|
|
$generated_guess = (strpos($new_corpus_block, '@'.'generated') !== false);
|
|
|
|
|
|
|
|
if (!$generated_guess) {
|
|
|
|
$config_key = 'differential.generated-paths';
|
|
|
|
$generated_path_regexps = PhabricatorEnv::getEnvConfig($config_key);
|
|
|
|
foreach ($generated_path_regexps as $regexp) {
|
|
|
|
if (preg_match($regexp, $this->changeset->getFilename())) {
|
|
|
|
$generated_guess = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$event = new PhabricatorEvent(
|
|
|
|
PhabricatorEventType::TYPE_DIFFERENTIAL_WILLMARKGENERATED,
|
|
|
|
array(
|
|
|
|
'corpus' => $new_corpus_block,
|
|
|
|
'is_generated' => $generated_guess
|
|
|
|
)
|
|
|
|
);
|
|
|
|
PhutilEventEngine::dispatchEvent($event);
|
|
|
|
|
|
|
|
$generated = $event->getValue('is_generated');
|
|
|
|
$this->specialAttributes[self::ATTR_GENERATED] = $generated;
|
|
|
|
|
|
|
|
if ($this->isTopLevel && !$this->comments &&
|
|
|
|
($this->isGenerated() || $this->isUnchanged() || $this->isDeleted())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-21 05:40:00 +02:00
|
|
|
$old_future = $this->getHighlightFuture($old_corpus_block);
|
|
|
|
$new_future = $this->getHighlightFuture($new_corpus_block);
|
|
|
|
$futures = array(
|
|
|
|
'old' => $old_future,
|
|
|
|
'new' => $new_future,
|
|
|
|
);
|
2012-04-13 08:52:50 +02:00
|
|
|
$corpus_blocks = array(
|
|
|
|
'old' => $old_corpus_block,
|
|
|
|
'new' => $new_corpus_block,
|
|
|
|
);
|
|
|
|
$this->highlightErrors = false;
|
2011-05-21 05:40:00 +02:00
|
|
|
foreach (Futures($futures) as $key => $future) {
|
2011-05-26 23:17:12 +02:00
|
|
|
try {
|
2012-04-13 08:52:50 +02:00
|
|
|
try {
|
|
|
|
$highlighted = $future->resolve();
|
|
|
|
} catch (PhutilSyntaxHighlighterException $ex) {
|
|
|
|
$this->highlightErrors = true;
|
|
|
|
$highlighted = id(new PhutilDefaultSyntaxHighlighter())
|
|
|
|
->getHighlightFuture($corpus_blocks[$key])
|
|
|
|
->resolve();
|
|
|
|
}
|
2011-05-26 23:17:12 +02:00
|
|
|
switch ($key) {
|
|
|
|
case 'old':
|
|
|
|
$this->oldRender = $this->processHighlightedSource(
|
|
|
|
$this->old,
|
2012-04-13 08:52:50 +02:00
|
|
|
$highlighted);
|
2011-05-26 23:17:12 +02:00
|
|
|
break;
|
|
|
|
case 'new':
|
|
|
|
$this->newRender = $this->processHighlightedSource(
|
|
|
|
$this->new,
|
2012-04-13 08:52:50 +02:00
|
|
|
$highlighted);
|
2011-05-26 23:17:12 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
phlog($ex);
|
|
|
|
throw $ex;
|
2011-05-21 06:15:00 +02:00
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->applyIntraline(
|
|
|
|
$this->oldRender,
|
|
|
|
ipull($this->intra, 0),
|
|
|
|
$old_corpus);
|
|
|
|
$this->applyIntraline(
|
|
|
|
$this->newRender,
|
|
|
|
ipull($this->intra, 1),
|
|
|
|
$new_corpus);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function loadCache() {
|
2011-05-05 16:08:10 +02:00
|
|
|
$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',
|
2011-05-05 16:08:10 +02:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
2012-05-01 07:31:26 +02:00
|
|
|
// Someone displays contents of a partially cached shielded file.
|
|
|
|
if (!isset($data['newRender']) && (!$this->isTopLevel || $this->comments)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
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() {
|
2012-04-13 08:52:50 +02:00
|
|
|
if ($this->highlightErrors) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-05-05 16:08:10 +02:00
|
|
|
$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-08-16 23:44:13 +02:00
|
|
|
|
|
|
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
2011-01-25 00:52:35 +01:00
|
|
|
queryfx(
|
2011-01-26 01:10:36 +01:00
|
|
|
$conn_w,
|
2011-07-09 00:26:33 +02:00
|
|
|
'INSERT INTO %T (id, cache, dateCreated) VALUES (%d, %s, %d)
|
2011-01-25 00:52:35 +01:00
|
|
|
ON DUPLICATE KEY UPDATE cache = VALUES(cache)',
|
2011-07-09 00:26:33 +02:00
|
|
|
DifferentialChangeset::TABLE_CACHE,
|
2011-05-05 16:08:10 +02:00
|
|
|
$render_cache_key,
|
2011-07-09 00:26:33 +02:00
|
|
|
$cache,
|
|
|
|
time());
|
2011-02-27 05:57:21 +01:00
|
|
|
} 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) {
|
2012-01-17 18:42:30 +01:00
|
|
|
|
|
|
|
$line_break = "<span class=\"over-the-line\">\xE2\xAC\x85</span><br />";
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
foreach ($render as $key => $text) {
|
|
|
|
if (isset($intra[$key])) {
|
|
|
|
$render[$key] = ArcanistDiffUtils::applyIntralineDiff(
|
|
|
|
$text,
|
|
|
|
$intra[$key]);
|
|
|
|
}
|
2011-06-08 21:39:03 +02:00
|
|
|
if (isset($corpus[$key]) && strlen($corpus[$key]) > $this->lineWidth) {
|
2012-01-17 18:42:30 +01:00
|
|
|
$lines = phutil_utf8_hard_wrap_html($render[$key], $this->lineWidth);
|
|
|
|
$render[$key] = implode($line_break, $lines);
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-21 06:15:00 +02:00
|
|
|
protected function getHighlightFuture($corpus) {
|
|
|
|
return $this->highlightEngine->getHighlightFuture(
|
2011-07-06 20:10:40 +02:00
|
|
|
$this->highlightEngine->getLanguageFromFilename($this->filename),
|
2011-01-25 02:39:14 +01:00
|
|
|
$corpus);
|
2011-05-21 06:15:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function processHighlightedSource($data, $result) {
|
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:
|
2012-03-13 01:06:36 +01:00
|
|
|
case self::WHITESPACE_IGNORE_FORCE:
|
2011-01-25 00:52:35 +01:00
|
|
|
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()) {
|
Don't use the "ignore all whitespace" algorithm on multi-hunk diffs
Summary:
Diffs with missing context don't render properly in the "ignore all whitespace"
algorith, so don't try to use it. These diffs can occur if someone creates a
diff via the web interface, for example, or if they muck around in their copy of
'arc'.
See D473, T246 (a problem with D473), rPe5bb756b5191720 (revert of D473) and
T231.
Test Plan:
Viewed a diff with missing context from the web interface. Verified normal diffs
still rendered with all whitespace ignored.
Reviewed By: fratrik
Reviewers: jungejason, aran, tuomaspelkonen, fratrik
Commenters: jungejason
CC: aran, epriestley, fratrik, jungejason
Differential Revision: 500
2011-06-23 01:12:09 +02:00
|
|
|
|
2012-03-13 01:06:36 +01:00
|
|
|
$ignore_all = (($whitespace_mode == self::WHITESPACE_IGNORE_ALL) ||
|
|
|
|
($whitespace_mode == self::WHITESPACE_IGNORE_FORCE));
|
|
|
|
|
|
|
|
$force_ignore = ($whitespace_mode == self::WHITESPACE_IGNORE_FORCE);
|
2011-06-29 01:21:06 +02:00
|
|
|
|
2012-03-13 01:06:36 +01:00
|
|
|
if (!$force_ignore) {
|
|
|
|
if ($ignore_all && $changeset->getWhitespaceMatters()) {
|
|
|
|
$ignore_all = false;
|
|
|
|
}
|
2011-07-22 22:15:11 +02:00
|
|
|
}
|
|
|
|
|
Don't use the "ignore all whitespace" algorithm on multi-hunk diffs
Summary:
Diffs with missing context don't render properly in the "ignore all whitespace"
algorith, so don't try to use it. These diffs can occur if someone creates a
diff via the web interface, for example, or if they muck around in their copy of
'arc'.
See D473, T246 (a problem with D473), rPe5bb756b5191720 (revert of D473) and
T231.
Test Plan:
Viewed a diff with missing context from the web interface. Verified normal diffs
still rendered with all whitespace ignored.
Reviewed By: fratrik
Reviewers: jungejason, aran, tuomaspelkonen, fratrik
Commenters: jungejason
CC: aran, epriestley, fratrik, jungejason
Differential Revision: 500
2011-06-23 01:12:09 +02:00
|
|
|
// The "ignore all whitespace" algorithm depends on rediffing the
|
|
|
|
// files, and we currently need complete representations of both
|
|
|
|
// files to do anything reasonable. If we only have parts of the files,
|
|
|
|
// don't use the "ignore all" algorithm.
|
2011-06-29 01:21:06 +02:00
|
|
|
if ($ignore_all) {
|
|
|
|
$hunks = $changeset->getHunks();
|
|
|
|
if (count($hunks) !== 1) {
|
|
|
|
$ignore_all = false;
|
|
|
|
} else {
|
|
|
|
$first_hunk = reset($hunks);
|
|
|
|
if ($first_hunk->getOldOffset() != 1 ||
|
|
|
|
$first_hunk->getNewOffset() != 1) {
|
|
|
|
$ignore_all = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($ignore_all) {
|
|
|
|
$old_file = $changeset->makeOldFile();
|
|
|
|
$new_file = $changeset->makeNewFile();
|
|
|
|
if ($old_file == $new_file) {
|
|
|
|
// If the old and new files are exactly identical, the synthetic
|
|
|
|
// diff below will give us nonsense and whitespace modes are
|
|
|
|
// irrelevant anyway. This occurs when you, e.g., copy a file onto
|
|
|
|
// itself in Subversion (see T271).
|
|
|
|
$ignore_all = false;
|
Don't use the "ignore all whitespace" algorithm on multi-hunk diffs
Summary:
Diffs with missing context don't render properly in the "ignore all whitespace"
algorith, so don't try to use it. These diffs can occur if someone creates a
diff via the web interface, for example, or if they muck around in their copy of
'arc'.
See D473, T246 (a problem with D473), rPe5bb756b5191720 (revert of D473) and
T231.
Test Plan:
Viewed a diff with missing context from the web interface. Verified normal diffs
still rendered with all whitespace ignored.
Reviewed By: fratrik
Reviewers: jungejason, aran, tuomaspelkonen, fratrik
Commenters: jungejason
CC: aran, epriestley, fratrik, jungejason
Differential Revision: 500
2011-06-23 01:12:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-29 01:21:06 +02:00
|
|
|
if ($ignore_all) {
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
2011-07-17 20:06:02 +02:00
|
|
|
$engine = new PhabricatorDifferenceEngine();
|
|
|
|
$engine->setIgnoreWhitespace(true);
|
|
|
|
$no_whitespace_changeset = $engine->generateChangesetFromFileContent(
|
|
|
|
$old_file,
|
|
|
|
$new_file);
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2011-04-28 23:32:29 +02:00
|
|
|
// subparser takes over the current non-whitespace-ignoring changeset
|
2011-05-21 05:40:00 +02:00
|
|
|
$subparser = new DifferentialChangesetParser();
|
|
|
|
$subparser->isSubparser = true;
|
|
|
|
$subparser->setChangeset($changeset);
|
2011-04-28 23:32:29 +02:00
|
|
|
foreach ($changeset->getHunks() as $hunk) {
|
2011-05-21 05:40:00 +02:00
|
|
|
$subparser->parseHunk($hunk);
|
2011-04-28 23:32:29 +02:00
|
|
|
}
|
2011-05-21 05:40:00 +02:00
|
|
|
// We need to call process() so that the subparser's values for
|
2011-07-17 20:06:02 +02:00
|
|
|
// metadata (like 'unchanged') is correct.
|
2011-05-21 05:40:00 +02:00
|
|
|
$subparser->process();
|
|
|
|
|
|
|
|
$this->subparser = $subparser;
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2011-05-21 05:40:00 +02:00
|
|
|
// While we aren't updating $this->changeset (since it has a bunch
|
|
|
|
// of metadata we need to preserve, so that headers like "this file
|
|
|
|
// was moved" render correctly), we're overwriting the local
|
|
|
|
// $changeset so that the block below will choose the synthetic
|
|
|
|
// hunks we've built instead of the original hunks.
|
2011-07-17 20:06:02 +02:00
|
|
|
$changeset = $no_whitespace_changeset;
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
2011-05-21 05:40:00 +02:00
|
|
|
|
|
|
|
// This either uses the real hunks, or synthetic hunks we built above.
|
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()) {
|
|
|
|
|
2011-06-20 22:49:17 +02:00
|
|
|
// "Top level" renders are initial requests for the whole file, versus
|
|
|
|
// requests for a specific range generated by clicking "show more". We
|
|
|
|
// generate property changes and "shield" UI elements only for toplevel
|
|
|
|
// requests.
|
|
|
|
$this->isTopLevel = (($range_start === null) && ($range_len === null));
|
|
|
|
|
2011-07-06 21:12:17 +02:00
|
|
|
$this->highlightEngine = PhabricatorSyntaxHighlighter::newEngine();
|
2011-01-25 02:39:14 +01:00
|
|
|
|
2012-05-01 07:31:26 +02:00
|
|
|
$shield = null;
|
|
|
|
if ($this->isTopLevel && !$this->comments &&
|
|
|
|
!($this->isGenerated() || $this->isUnchanged() || $this->isDeleted()) &&
|
|
|
|
$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 {
|
|
|
|
|
|
|
|
$this->tryCacheStuff();
|
|
|
|
|
|
|
|
if ($this->isTopLevel && !$this->comments) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($shield) {
|
|
|
|
return $this->renderChangesetTable($this->changeset, $shield);
|
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
$feedback_mask = array();
|
|
|
|
|
|
|
|
switch ($this->changeset->getFileType()) {
|
|
|
|
case DifferentialChangeType::FILE_IMAGE:
|
|
|
|
$old = null;
|
|
|
|
$cur = null;
|
2011-09-19 08:04:03 +02:00
|
|
|
// TODO: Improve the architectural issue as discussed in D955
|
|
|
|
// https://secure.phabricator.com/D955
|
|
|
|
$reference = $this->renderingReference;
|
|
|
|
$parts = explode('/', $reference);
|
|
|
|
if (count($parts) == 2) {
|
|
|
|
list($id, $vs) = $parts;
|
|
|
|
} else {
|
|
|
|
$id = $parts[0];
|
|
|
|
$vs = 0;
|
|
|
|
}
|
|
|
|
$id = (int)$id;
|
|
|
|
$vs = (int)$vs;
|
|
|
|
|
|
|
|
if (!$vs) {
|
|
|
|
$metadata = $this->changeset->getMetadata();
|
|
|
|
$data = idx($metadata, 'attachment-data');
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2011-09-19 08:04:03 +02:00
|
|
|
$old_phid = idx($metadata, 'old:binary-phid');
|
|
|
|
$new_phid = idx($metadata, 'new:binary-phid');
|
|
|
|
} else {
|
|
|
|
$vs_changeset = id(new DifferentialChangeset())->load($vs);
|
|
|
|
$vs_metadata = $vs_changeset->getMetadata();
|
|
|
|
$old_phid = idx($vs_metadata, 'new:binary-phid');
|
|
|
|
|
|
|
|
$changeset = id(new DifferentialChangeset())->load($id);
|
|
|
|
$metadata = $changeset->getMetadata();
|
|
|
|
$new_phid = idx($metadata, 'new:binary-phid');
|
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
if ($old_phid || $new_phid) {
|
2012-01-10 23:48:55 +01:00
|
|
|
|
|
|
|
// grab the files, (micro) optimization for 1 query not 2
|
|
|
|
$file_phids = array();
|
2011-01-25 00:52:35 +01:00
|
|
|
if ($old_phid) {
|
2012-01-10 23:48:55 +01:00
|
|
|
$file_phids[] = $old_phid;
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
if ($new_phid) {
|
2012-01-10 23:48:55 +01:00
|
|
|
$file_phids[] = $new_phid;
|
|
|
|
}
|
|
|
|
|
|
|
|
$files = id(new PhabricatorFile())->loadAllWhere(
|
|
|
|
'phid IN (%Ls)',
|
|
|
|
$file_phids);
|
|
|
|
|
|
|
|
foreach ($files as $file) {
|
|
|
|
if (empty($file)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ($file->getPHID() == $old_phid) {
|
|
|
|
$old = phutil_render_tag(
|
|
|
|
'img',
|
|
|
|
array(
|
|
|
|
'src' => $file->getBestURI(),
|
|
|
|
));
|
|
|
|
} else {
|
|
|
|
$cur = phutil_render_tag(
|
|
|
|
'img',
|
|
|
|
array(
|
|
|
|
'src' => $file->getBestURI(),
|
|
|
|
));
|
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-01 08:18:38 +02:00
|
|
|
$this->comments = msort($this->comments, 'getID');
|
|
|
|
$old_comments = array();
|
|
|
|
$new_comments = array();
|
|
|
|
foreach ($this->comments as $comment) {
|
|
|
|
if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
|
|
|
|
$new_comments[] = $comment;
|
|
|
|
} else {
|
|
|
|
$old_comments[] = $comment;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$html_old = array();
|
|
|
|
$html_new = array();
|
|
|
|
foreach ($old_comments as $comment) {
|
|
|
|
$xhp = $this->renderInlineComment($comment);
|
|
|
|
$html_old[] =
|
|
|
|
'<tr class="inline"><th /><td>'.
|
|
|
|
$xhp.
|
|
|
|
'</td><th /><td /></tr>';
|
|
|
|
}
|
|
|
|
foreach ($new_comments as $comment) {
|
|
|
|
$xhp = $this->renderInlineComment($comment);
|
|
|
|
$html_new[] =
|
|
|
|
'<tr class="inline"><th /><td /><th /><td>'.
|
|
|
|
$xhp.
|
|
|
|
'</td></tr>';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$old) {
|
|
|
|
$th_old = '<th></th>';
|
|
|
|
}
|
|
|
|
else {
|
2011-09-19 08:04:03 +02:00
|
|
|
$th_old = '<th id="C'.$vs.'OL1">1</th>';
|
2011-09-01 08:18:38 +02:00
|
|
|
}
|
|
|
|
if (!$cur) {
|
|
|
|
$th_new = '<th></th>';
|
|
|
|
}
|
|
|
|
else {
|
2011-09-19 08:04:03 +02:00
|
|
|
$th_new = '<th id="C'.$id.'NL1">1</th>';
|
2011-09-01 08:18:38 +02:00
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
$output = $this->renderChangesetTable(
|
|
|
|
$this->changeset,
|
|
|
|
'<tr>'.
|
2011-09-01 08:18:38 +02:00
|
|
|
$th_old.
|
2011-01-25 00:52:35 +01:00
|
|
|
'<td class="differential-old-image">'.
|
|
|
|
'<div class="differential-image-stage">'.
|
|
|
|
$old.
|
|
|
|
'</div>'.
|
|
|
|
'</td>'.
|
2011-09-01 08:18:38 +02:00
|
|
|
$th_new.
|
2011-01-25 00:52:35 +01:00
|
|
|
'<td class="differential-new-image">'.
|
|
|
|
'<div class="differential-image-stage">'.
|
|
|
|
$cur.
|
|
|
|
'</div>'.
|
|
|
|
'</td>'.
|
2011-09-01 08:18:38 +02:00
|
|
|
'</tr>'.
|
|
|
|
implode('', $html_old).
|
|
|
|
implode('', $html_new));
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
return $output;
|
|
|
|
case DifferentialChangeType::FILE_DIRECTORY:
|
|
|
|
case DifferentialChangeType::FILE_BINARY:
|
|
|
|
$output = $this->renderChangesetTable($this->changeset, null);
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
|
|
|
$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;
|
2011-05-09 06:22:25 +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();
|
2011-05-09 06:22:25 +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);
|
|
|
|
}
|
|
|
|
|
2011-05-09 06:22:25 +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.
|
|
|
|
*
|
Add inline comments to Diffusion/Audit
Summary:
- Add inline comments to Audits, like Differential.
- Creates new storage for the comments in the Audits database.
- Creates a new PhabricatorAuditInlineComment class, similar to DifferentialInlineComment.
- Defines an Interface which Differential and Audit comments conform to.
- Makes consumers of DifferentialInlineComments consume objects which implement that interface instead.
- Adds save
NOTE: Some features are still missing! Wanted to cut this off before it got crazy:
- Inline comments aren't shown in the main comment list.
- Inline comments aren't shown in the emails.
- Inline comments aren't previewed.
I'll followup with those but this was getting pretty big.
@vrana, does the SQL change look correct?
Test Plan:
- Created, edited, deleted, replied to, reloaded and saved inline comments in Diffusion, on the left and right side of diffs.
- Created, edited, deleted, replied to, reloaded and saved inline comments in Differentila, on the left and right side of primary and diff-versus-diff diffs.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T904
Differential Revision: https://secure.phabricator.com/D1898
2012-03-14 20:56:01 +01:00
|
|
|
* @param PhabricatorInlineCommentInterface Comment to test for visibility.
|
2011-05-09 06:22:25 +02:00
|
|
|
* @return bool True if the comment is visible on the rendered diff.
|
|
|
|
*/
|
|
|
|
private function isCommentVisibleOnRenderedDiff(
|
Add inline comments to Diffusion/Audit
Summary:
- Add inline comments to Audits, like Differential.
- Creates new storage for the comments in the Audits database.
- Creates a new PhabricatorAuditInlineComment class, similar to DifferentialInlineComment.
- Defines an Interface which Differential and Audit comments conform to.
- Makes consumers of DifferentialInlineComments consume objects which implement that interface instead.
- Adds save
NOTE: Some features are still missing! Wanted to cut this off before it got crazy:
- Inline comments aren't shown in the main comment list.
- Inline comments aren't shown in the emails.
- Inline comments aren't previewed.
I'll followup with those but this was getting pretty big.
@vrana, does the SQL change look correct?
Test Plan:
- Created, edited, deleted, replied to, reloaded and saved inline comments in Diffusion, on the left and right side of diffs.
- Created, edited, deleted, replied to, reloaded and saved inline comments in Differentila, on the left and right side of primary and diff-versus-diff diffs.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T904
Differential Revision: https://secure.phabricator.com/D1898
2012-03-14 20:56:01 +01:00
|
|
|
PhabricatorInlineCommentInterface $comment) {
|
2011-05-09 06:22:25 +02:00
|
|
|
|
|
|
|
$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().
|
|
|
|
*
|
Add inline comments to Diffusion/Audit
Summary:
- Add inline comments to Audits, like Differential.
- Creates new storage for the comments in the Audits database.
- Creates a new PhabricatorAuditInlineComment class, similar to DifferentialInlineComment.
- Defines an Interface which Differential and Audit comments conform to.
- Makes consumers of DifferentialInlineComments consume objects which implement that interface instead.
- Adds save
NOTE: Some features are still missing! Wanted to cut this off before it got crazy:
- Inline comments aren't shown in the main comment list.
- Inline comments aren't shown in the emails.
- Inline comments aren't previewed.
I'll followup with those but this was getting pretty big.
@vrana, does the SQL change look correct?
Test Plan:
- Created, edited, deleted, replied to, reloaded and saved inline comments in Diffusion, on the left and right side of diffs.
- Created, edited, deleted, replied to, reloaded and saved inline comments in Differentila, on the left and right side of primary and diff-versus-diff diffs.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T904
Differential Revision: https://secure.phabricator.com/D1898
2012-03-14 20:56:01 +01:00
|
|
|
* @param PhabricatorInlineCommentInterface Comment to test for display
|
|
|
|
* location.
|
2011-05-09 06:22:25 +02:00
|
|
|
* @return bool True for right, false for left.
|
|
|
|
*/
|
|
|
|
private function isCommentOnRightSideWhenDisplayed(
|
Add inline comments to Diffusion/Audit
Summary:
- Add inline comments to Audits, like Differential.
- Creates new storage for the comments in the Audits database.
- Creates a new PhabricatorAuditInlineComment class, similar to DifferentialInlineComment.
- Defines an Interface which Differential and Audit comments conform to.
- Makes consumers of DifferentialInlineComments consume objects which implement that interface instead.
- Adds save
NOTE: Some features are still missing! Wanted to cut this off before it got crazy:
- Inline comments aren't shown in the main comment list.
- Inline comments aren't shown in the emails.
- Inline comments aren't previewed.
I'll followup with those but this was getting pretty big.
@vrana, does the SQL change look correct?
Test Plan:
- Created, edited, deleted, replied to, reloaded and saved inline comments in Diffusion, on the left and right side of diffs.
- Created, edited, deleted, replied to, reloaded and saved inline comments in Differentila, on the left and right side of primary and diff-versus-diff diffs.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T904
Differential Revision: https://secure.phabricator.com/D1898
2012-03-14 20:56:01 +01:00
|
|
|
PhabricatorInlineCommentInterface $comment) {
|
2011-05-09 06:22:25 +02:00
|
|
|
|
|
|
|
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
|
|
|
}
|
2011-05-09 06:22:25 +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();
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
$reference = $this->renderingReference;
|
2011-01-25 00:52:35 +01:00
|
|
|
$more =
|
|
|
|
' '.
|
2011-02-01 05:38:13 +01:00
|
|
|
javelin_render_tag(
|
2011-01-25 00:52:35 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'mustcapture' => true,
|
|
|
|
'sigil' => 'show-more',
|
|
|
|
'class' => 'complete',
|
2011-02-01 05:38:13 +01:00
|
|
|
'href' => '#',
|
|
|
|
'meta' => array(
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
'ref' => $reference,
|
|
|
|
'range' => "0-{$end}",
|
2011-02-01 05:38:13 +01:00
|
|
|
),
|
2011-01-25 00:52:35 +01:00
|
|
|
),
|
|
|
|
'Show File Contents');
|
|
|
|
} else {
|
|
|
|
$more = null;
|
|
|
|
}
|
|
|
|
|
2011-02-01 05:38:13 +01:00
|
|
|
return javelin_render_tag(
|
2011-01-25 00:52:35 +01:00
|
|
|
'tr',
|
|
|
|
array(
|
2011-02-01 05:38:13 +01:00
|
|
|
'sigil' => 'context-target',
|
2011-01-25 00:52:35 +01:00
|
|
|
),
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
'<td class="differential-shield" colspan="5">'.
|
2011-01-25 00:52:35 +01:00
|
|
|
phutil_escape_html($message).
|
|
|
|
$more.
|
|
|
|
'</td>');
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function renderTextChange(
|
|
|
|
$range_start,
|
|
|
|
$range_len,
|
|
|
|
$mask_force,
|
|
|
|
$feedback_mask,
|
|
|
|
array $old_comments,
|
|
|
|
array $new_comments) {
|
2012-04-04 22:13:08 +02:00
|
|
|
foreach (array_merge($old_comments, $new_comments) as $comments) {
|
|
|
|
assert_instances_of($comments, 'PhabricatorInlineCommentInterface');
|
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
$context_not_available = null;
|
|
|
|
if ($this->missingOld || $this->missingNew) {
|
2011-02-01 05:38:13 +01:00
|
|
|
$context_not_available = javelin_render_tag(
|
2011-01-25 00:52:35 +01:00
|
|
|
'tr',
|
|
|
|
array(
|
|
|
|
'sigil' => 'context-target',
|
|
|
|
),
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
'<td colspan="5" class="show-more">'.
|
2012-04-10 01:57:17 +02:00
|
|
|
'Context not available.'.
|
2011-01-25 00:52:35 +01:00
|
|
|
'</td>');
|
|
|
|
}
|
|
|
|
|
|
|
|
$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);
|
|
|
|
|
2011-05-09 06:22:25 +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);
|
|
|
|
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
$reference = $this->renderingReference;
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2011-05-09 06:22:25 +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';
|
|
|
|
|
2012-04-26 09:18:42 +02:00
|
|
|
$copy_lines = idx($this->changeset->getMetadata(), 'copy:lines', array());
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
|
|
|
|
if (empty($mask[$ii])) {
|
2011-05-09 06:22:25 +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) {
|
2011-07-29 22:46:53 +02:00
|
|
|
$is_first_block = false;
|
|
|
|
if ($ii == 0) {
|
|
|
|
$is_first_block = true;
|
|
|
|
}
|
|
|
|
|
2011-02-01 05:38:13 +01:00
|
|
|
$contents[] = javelin_render_tag(
|
2011-01-25 00:52:35 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '#',
|
|
|
|
'mustcapture' => true,
|
|
|
|
'sigil' => 'show-more',
|
2011-02-01 05:38:13 +01:00
|
|
|
'meta' => array(
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
'ref' => $reference,
|
2011-02-01 05:38:13 +01:00
|
|
|
'range' => "{$top}-{$len}/{$top}-20",
|
|
|
|
),
|
2011-01-25 00:52:35 +01:00
|
|
|
),
|
2011-07-29 22:46:53 +02:00
|
|
|
$is_first_block
|
|
|
|
? "Show First 20 Lines"
|
|
|
|
: "\xE2\x96\xB2 Show 20 Lines");
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2011-02-01 05:38:13 +01:00
|
|
|
$contents[] = javelin_render_tag(
|
2011-01-25 00:52:35 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '#',
|
|
|
|
'mustcapture' => true,
|
|
|
|
'sigil' => 'show-more',
|
2011-02-01 05:38:13 +01:00
|
|
|
'meta' => array(
|
2012-01-06 05:29:16 +01:00
|
|
|
'type' => 'all',
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
'ref' => $reference,
|
2012-01-06 05:29:16 +01:00
|
|
|
'range' => "{$top}-{$len}/{$top}-{$len}",
|
2011-02-01 05:38:13 +01:00
|
|
|
),
|
2011-01-25 00:52:35 +01:00
|
|
|
),
|
|
|
|
'Show All '.$len.' Lines');
|
|
|
|
|
|
|
|
if ($len > 40) {
|
2011-07-29 22:46:53 +02:00
|
|
|
$is_last_block = false;
|
|
|
|
if ($ii + $len >= $rows) {
|
|
|
|
$is_last_block = true;
|
|
|
|
}
|
|
|
|
|
2011-02-01 05:38:13 +01:00
|
|
|
$contents[] = javelin_render_tag(
|
2011-01-25 00:52:35 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '#',
|
|
|
|
'mustcapture' => true,
|
|
|
|
'sigil' => 'show-more',
|
2011-02-01 05:38:13 +01:00
|
|
|
'meta' => array(
|
Move "Rendering References" to the DifferentialChangesetParser level
Summary:
Separates changeset IDs from rendering. Now each changeset has a "rendering
reference" which is basically a description of what the ajax endpoint should
render. For Differential, it's in the form "id/vs". For Diffusion,
"branch/path;commit".
I believe this fixes pretty much all of the bugs related to "show more" breaking
in various obscure ways, although I never got a great repro for T153.
Test Plan:
Clicked "show more" in diffusion change and commit views and differential diff,
diff-of-diff, standalone-diff, standalone-diff-of-diff views. Verified refs and
'whitespace' were always sent correctly.
Made inline comments on diffs and diffs-of-diffs. Used "Reply".
Reviewed By: tuomaspelkonen
Reviewers: tuomaspelkonen, jungejason, aran
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 274
2011-05-12 06:46:29 +02:00
|
|
|
'ref' => $reference,
|
2011-02-01 05:38:13 +01:00
|
|
|
'range' => "{$top}-{$len}/{$end}-20",
|
|
|
|
),
|
2011-01-25 00:52:35 +01:00
|
|
|
),
|
2011-07-29 22:46:53 +02:00
|
|
|
$is_last_block
|
|
|
|
? "Show Last 20 Lines"
|
|
|
|
: "\xE2\x96\xBC Show 20 Lines");
|
2011-01-25 00:52:35 +01:00
|
|
|
};
|
|
|
|
|
2011-02-01 05:38:13 +01:00
|
|
|
$container = javelin_render_tag(
|
2011-01-25 00:52:35 +01:00
|
|
|
'tr',
|
|
|
|
array(
|
|
|
|
'sigil' => 'context-target',
|
|
|
|
),
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
'<td colspan="5" class="show-more">'.
|
2011-01-25 00:52:35 +01:00
|
|
|
implode(' • ', $contents).
|
|
|
|
'</td>');
|
|
|
|
|
|
|
|
$html[] = $container;
|
|
|
|
|
|
|
|
$ii += ($len - 1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($this->old[$ii])) {
|
|
|
|
$o_num = $this->old[$ii]['line'];
|
2011-02-05 02:53:14 +01:00
|
|
|
$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']) {
|
2012-03-15 00:45:40 +01:00
|
|
|
if ($this->old[$ii]['type'] == '\\') {
|
2012-03-15 02:24:29 +01:00
|
|
|
$o_text = $this->old[$ii]['text'];
|
2012-03-15 00:45:40 +01:00
|
|
|
$o_attr = ' class="comment"';
|
2012-04-05 00:06:45 +02:00
|
|
|
} else if (empty($this->new[$ii])) {
|
2011-01-25 00:52:35 +01:00
|
|
|
$o_attr = ' class="old old-full"';
|
|
|
|
} else {
|
|
|
|
$o_attr = ' class="old"';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$o_num = null;
|
|
|
|
$o_text = null;
|
|
|
|
$o_attr = null;
|
|
|
|
}
|
|
|
|
|
2012-05-05 02:41:06 +02:00
|
|
|
$n_copy = '<td class="copy"></td>';
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
if (isset($this->new[$ii])) {
|
|
|
|
$n_num = $this->new[$ii]['line'];
|
2011-02-05 02:53:14 +01:00
|
|
|
$n_text = isset($this->newRender[$ii]) ? $this->newRender[$ii] : null;
|
2011-01-25 00:52:35 +01:00
|
|
|
$n_attr = null;
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
|
|
|
|
$cov_class = null;
|
|
|
|
if ($this->coverage !== null) {
|
|
|
|
if (empty($this->coverage[$n_num - 1])) {
|
|
|
|
$cov_class = 'N';
|
|
|
|
} else {
|
|
|
|
$cov_class = $this->coverage[$n_num - 1];
|
|
|
|
}
|
|
|
|
$cov_class = 'cov-'.$cov_class;
|
|
|
|
}
|
|
|
|
|
|
|
|
$n_cov = '<td class="cov '.$cov_class.'"></td>';
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
if ($this->new[$ii]['type']) {
|
2012-03-15 00:45:40 +01:00
|
|
|
if ($this->new[$ii]['type'] == '\\') {
|
2012-03-15 13:19:46 +01:00
|
|
|
$n_text = $this->new[$ii]['text'];
|
2012-05-05 02:41:06 +02:00
|
|
|
$n_class = 'comment';
|
|
|
|
} else if (empty($this->old[$ii])) {
|
|
|
|
$n_class = 'new new-full';
|
|
|
|
} else {
|
|
|
|
$n_class = 'new';
|
|
|
|
}
|
|
|
|
$n_attr = ' class="'.$n_class.'"';
|
|
|
|
|
|
|
|
if ($this->new[$ii]['type'] == '\\' || !isset($copy_lines[$n_num])) {
|
|
|
|
$n_copy = '<td class="copy '.$n_class.'"></td>';
|
|
|
|
} else {
|
2012-04-30 06:35:43 +02:00
|
|
|
list($orig_file, $orig_line, $orig_type) = $copy_lines[$n_num];
|
|
|
|
$title = ($orig_type == '-' ? 'Moved' : 'Copied').' from ';
|
2012-04-26 09:18:42 +02:00
|
|
|
if ($orig_file == '') {
|
2012-04-30 06:35:43 +02:00
|
|
|
$title .= "line {$orig_line}";
|
2012-04-26 09:18:42 +02:00
|
|
|
} else {
|
2012-04-30 06:35:43 +02:00
|
|
|
$title .=
|
2012-04-26 09:18:42 +02:00
|
|
|
basename($orig_file).
|
|
|
|
":{$orig_line} in dir ".
|
|
|
|
dirname('/'.$orig_file);
|
|
|
|
}
|
2012-04-30 06:35:43 +02:00
|
|
|
$class = ($orig_type == '-' ? 'new-move' : 'new-copy');
|
2012-05-05 02:41:06 +02:00
|
|
|
$n_copy = javelin_render_tag(
|
|
|
|
'td',
|
|
|
|
array(
|
|
|
|
'meta' => array(
|
|
|
|
'msg' => $title,
|
|
|
|
),
|
|
|
|
'class' => 'copy '.$class,
|
|
|
|
),
|
|
|
|
'');
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$n_num = null;
|
|
|
|
$n_text = null;
|
|
|
|
$n_attr = null;
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
$n_cov = null;
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (($o_num && !empty($this->missingOld[$o_num])) ||
|
|
|
|
($n_num && !empty($this->missingNew[$n_num]))) {
|
|
|
|
$html[] = $context_not_available;
|
|
|
|
}
|
|
|
|
|
2011-05-09 06:22:25 +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;
|
|
|
|
}
|
|
|
|
|
2011-05-09 06:22:25 +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>'.
|
2012-05-05 02:41:06 +02:00
|
|
|
$n_copy.
|
Kind-of-terrible (?) oncopy handler
Summary: Works in Safari, Firefox, Chrome.
Test Plan: Copied some text, threw up a little in my mouth.
Reviewers: aran, tuomaspelkonen, tomo, rstout, btrahan
Reviewed By: aran
CC: aran, epriestley, ddfisher
Maniphest Tasks: T145, T995
Differential Revision: https://secure.phabricator.com/D244
2012-03-16 03:04:59 +01:00
|
|
|
// NOTE: This is a unicode zero-width space, which we use as a hint
|
|
|
|
// when intercepting 'copy' events to make sure sensible text ends
|
|
|
|
// up on the clipboard. See the 'phabricator-oncopy' behavior.
|
|
|
|
'<td'.$n_attr.'>'."\xE2\x80\x8B".$n_text.'</td>'.
|
Show coverage information in Differential
Summary:
Render coverage information in the right gutter, if available.
We could render some kind of summary report deal too but this seems like a good
start.
Test Plan:
- Looked at diffs with coverage.
- Looked at diffs without coverage.
- Used inline comments, diff-of-diff, "show more", "show entire file", "show
generated file", "undo". Nothing seemed disrupted by the addition of a 5th
column.
Reviewers: btrahan, tuomaspelkonen, jungejason
Reviewed By: btrahan
CC: zeeg, aran, epriestley
Maniphest Tasks: T140
Differential Revision: https://secure.phabricator.com/D1527
2012-01-31 21:07:47 +01:00
|
|
|
$n_cov.
|
2011-01-25 00:52:35 +01:00
|
|
|
'</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);
|
2012-02-04 08:18:47 +01:00
|
|
|
$new = '';
|
|
|
|
if ($n_num && isset($new_comments[$n_num])) {
|
|
|
|
foreach ($new_comments[$n_num] as $key => $new_comment) {
|
|
|
|
if ($comment->isCompatible($new_comment)) {
|
|
|
|
$new = $this->renderInlineComment($new_comment);
|
|
|
|
unset($new_comments[$n_num][$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
$html[] =
|
|
|
|
'<tr class="inline"><th /><td>'.
|
|
|
|
$xhp.
|
2012-02-04 08:18:47 +01:00
|
|
|
'</td><th /><td>'.
|
|
|
|
$new.
|
|
|
|
'</td><td class="cov" /></tr>';
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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.
|
2012-02-01 03:30:17 +01:00
|
|
|
'</td><td class="cov" /></tr>';
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return implode('', $html);
|
|
|
|
}
|
|
|
|
|
Add inline comments to Diffusion/Audit
Summary:
- Add inline comments to Audits, like Differential.
- Creates new storage for the comments in the Audits database.
- Creates a new PhabricatorAuditInlineComment class, similar to DifferentialInlineComment.
- Defines an Interface which Differential and Audit comments conform to.
- Makes consumers of DifferentialInlineComments consume objects which implement that interface instead.
- Adds save
NOTE: Some features are still missing! Wanted to cut this off before it got crazy:
- Inline comments aren't shown in the main comment list.
- Inline comments aren't shown in the emails.
- Inline comments aren't previewed.
I'll followup with those but this was getting pretty big.
@vrana, does the SQL change look correct?
Test Plan:
- Created, edited, deleted, replied to, reloaded and saved inline comments in Diffusion, on the left and right side of diffs.
- Created, edited, deleted, replied to, reloaded and saved inline comments in Differentila, on the left and right side of primary and diff-versus-diff diffs.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T904
Differential Revision: https://secure.phabricator.com/D1898
2012-03-14 20:56:01 +01:00
|
|
|
private function renderInlineComment(
|
|
|
|
PhabricatorInlineCommentInterface $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()) &&
|
Add inline comments to Diffusion/Audit
Summary:
- Add inline comments to Audits, like Differential.
- Creates new storage for the comments in the Audits database.
- Creates a new PhabricatorAuditInlineComment class, similar to DifferentialInlineComment.
- Defines an Interface which Differential and Audit comments conform to.
- Makes consumers of DifferentialInlineComments consume objects which implement that interface instead.
- Adds save
NOTE: Some features are still missing! Wanted to cut this off before it got crazy:
- Inline comments aren't shown in the main comment list.
- Inline comments aren't shown in the emails.
- Inline comments aren't previewed.
I'll followup with those but this was getting pretty big.
@vrana, does the SQL change look correct?
Test Plan:
- Created, edited, deleted, replied to, reloaded and saved inline comments in Diffusion, on the left and right side of diffs.
- Created, edited, deleted, replied to, reloaded and saved inline comments in Differentila, on the left and right side of primary and diff-versus-diff diffs.
Reviewers: btrahan, vrana
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T904
Differential Revision: https://secure.phabricator.com/D1898
2012-03-14 20:56:01 +01:00
|
|
|
($comment->isDraft());
|
2012-04-18 06:18:30 +02:00
|
|
|
$allow_reply = (bool)$this->user;
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2011-05-09 06:22:25 +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)
|
2012-04-18 06:18:30 +02:00
|
|
|
->setAllowReply($allow_reply)
|
2011-02-02 01:42:36 +01:00
|
|
|
->render();
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function renderPropertyChangeHeader($changeset) {
|
2011-06-20 22:49:17 +02:00
|
|
|
if (!$this->isTopLevel) {
|
|
|
|
// We render properties only at top level; otherwise we get multiple
|
|
|
|
// copies of them when a user clicks "Show More".
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
|
|
|
$keys = array_keys($old + $new);
|
|
|
|
sort($keys);
|
2011-06-08 03:59:42 +02:00
|
|
|
|
|
|
|
$rows = array();
|
2011-01-25 00:52:35 +01:00
|
|
|
foreach ($keys as $key) {
|
|
|
|
$oval = idx($old, $key);
|
|
|
|
$nval = idx($new, $key);
|
|
|
|
if ($oval !== $nval) {
|
|
|
|
if ($oval === null) {
|
2011-06-08 03:59:42 +02:00
|
|
|
$oval = '<em>null</em>';
|
|
|
|
} else {
|
|
|
|
$oval = phutil_escape_html($oval);
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
2011-06-08 03:59:42 +02:00
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
if ($nval === null) {
|
2011-06-08 03:59:42 +02:00
|
|
|
$nval = '<em>null</em>';
|
|
|
|
} else {
|
|
|
|
$nval = phutil_escape_html($nval);
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
2011-06-08 03:59:42 +02:00
|
|
|
|
|
|
|
$rows[] =
|
|
|
|
'<tr>'.
|
|
|
|
'<th>'.phutil_escape_html($key).'</th>'.
|
|
|
|
'<td class="oval">'.$oval.'</td>'.
|
|
|
|
'<td class="nval">'.$nval.'</td>'.
|
|
|
|
'</tr>';
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-08 03:59:42 +02:00
|
|
|
return
|
|
|
|
'<table class="differential-property-table">'.
|
|
|
|
'<tr class="property-table-header">'.
|
|
|
|
'<th>Property Changes</th>'.
|
|
|
|
'<td class="oval">Old Value</td>'.
|
|
|
|
'<td class="nval">New Value</td>'.
|
|
|
|
'</tr>'.
|
|
|
|
implode('', $rows).
|
|
|
|
'</table>';
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function renderChangesetTable($changeset, $contents) {
|
|
|
|
$props = $this->renderPropertyChangeHeader($this->changeset);
|
|
|
|
$table = null;
|
|
|
|
if ($contents) {
|
2012-04-17 03:53:38 +02:00
|
|
|
$table = javelin_render_tag(
|
|
|
|
'table',
|
|
|
|
array(
|
|
|
|
'class' => 'differential-diff remarkup-code PhabricatorMonospaced',
|
|
|
|
'sigil' => 'differential-diff',
|
|
|
|
),
|
|
|
|
$contents);
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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',
|
2012-03-07 23:24:43 +01:00
|
|
|
DifferentialChangeType::FILE_SUBMODULE => 'submodule',
|
2011-01-25 00:52:35 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
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} ".
|
2012-01-16 07:25:53 +01:00
|
|
|
"<strong>".
|
|
|
|
phutil_escape_html($changeset->getOldFile()).
|
|
|
|
"</strong>.";
|
2011-01-25 00:52:35 +01:00
|
|
|
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}: ".
|
2012-01-16 07:25:53 +01:00
|
|
|
"<strong>".phutil_escape_html(implode(', ', $paths))."</strong>.";
|
2011-01-25 00:52:35 +01:00
|
|
|
} else {
|
|
|
|
$message =
|
|
|
|
"This {$files[$file]} was {$verb} ".
|
2012-01-16 07:25:53 +01:00
|
|
|
"<strong>".phutil_escape_html(reset($paths))."</strong>.";
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
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\">- %s</font><br/>",
|
|
|
|
str_replace(" ", " ", phutil_escape_html($o['text']))
|
|
|
|
);
|
|
|
|
$ret .= sprintf(
|
|
|
|
"<font color=\"green\">+ %s</font><br/>",
|
|
|
|
str_replace(" ", " ", phutil_escape_html($n['text']))
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$ret .= sprintf(" %s<br/>",
|
|
|
|
str_replace(" ", " ", phutil_escape_html($n['text']))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else if ($o['line'] && !$n['line']) {
|
|
|
|
$ret .= sprintf(
|
|
|
|
"<font color=\"red\">- %s</font><br/>",
|
|
|
|
str_replace(" ", " ", phutil_escape_html($o['text']))
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$ret .= sprintf(
|
|
|
|
"<font color=\"green\">+ %s</font><br/>",
|
|
|
|
str_replace(" ", " ", phutil_escape_html($n['text']))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2011-07-17 20:06:02 +02:00
|
|
|
/**
|
|
|
|
* Parse the 'range' specification that this class and the client-side JS
|
|
|
|
* emit to indicate that a user clicked "Show more..." on a diff. Generally,
|
|
|
|
* use is something like this:
|
|
|
|
*
|
|
|
|
* $spec = $request->getStr('range');
|
|
|
|
* $parsed = DifferentialChangesetParser::parseRangeSpecification($spec);
|
|
|
|
* list($start, $end, $mask) = $parsed;
|
|
|
|
* $parser->render($start, $end, $mask);
|
|
|
|
*
|
|
|
|
* @param string Range specification, indicating the range of the diff that
|
|
|
|
* should be rendered.
|
|
|
|
* @return tuple List of <start, end, mask> suitable for passing to
|
|
|
|
* @{method:render}.
|
|
|
|
*/
|
|
|
|
public static function parseRangeSpecification($spec) {
|
|
|
|
$range_s = null;
|
|
|
|
$range_e = null;
|
|
|
|
$mask = array();
|
|
|
|
|
|
|
|
if ($spec) {
|
|
|
|
$match = null;
|
|
|
|
if (preg_match('@^(\d+)-(\d+)(?:/(\d+)-(\d+))?$@', $spec, $match)) {
|
|
|
|
$range_s = (int)$match[1];
|
|
|
|
$range_e = (int)$match[2];
|
|
|
|
if (count($match) > 3) {
|
|
|
|
$start = (int)$match[3];
|
|
|
|
$len = (int)$match[4];
|
|
|
|
for ($ii = $start; $ii < $start + $len; $ii++) {
|
|
|
|
$mask[$ii] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array($range_s, $range_e, $mask);
|
|
|
|
}
|
|
|
|
|
2012-03-13 01:06:55 +01:00
|
|
|
/**
|
|
|
|
* Render "modified coverage" information; test coverage on modified lines.
|
|
|
|
* This synthesizes diff information with unit test information into a useful
|
|
|
|
* indicator of how well tested a change is.
|
|
|
|
*/
|
|
|
|
public function renderModifiedCoverage() {
|
|
|
|
$na = '<em>-</em>';
|
|
|
|
|
|
|
|
if (!$this->coverage) {
|
|
|
|
return $na;
|
|
|
|
}
|
|
|
|
|
|
|
|
$covered = 0;
|
|
|
|
$not_covered = 0;
|
|
|
|
|
|
|
|
foreach ($this->new as $k => $new) {
|
|
|
|
if (!$new['line']) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$new['type']) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (empty($this->coverage[$new['line'] - 1])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-03-13 01:10:05 +01:00
|
|
|
switch ($this->coverage[$new['line'] - 1]) {
|
2012-03-13 01:06:55 +01:00
|
|
|
case 'C':
|
|
|
|
$covered++;
|
|
|
|
break;
|
|
|
|
case 'U':
|
|
|
|
$not_covered++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$covered && !$not_covered) {
|
|
|
|
return $na;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered)));
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|