2011-01-25 00:52:35 +01:00
|
|
|
<?php
|
|
|
|
|
2012-03-13 19:18:11 +01:00
|
|
|
final class DifferentialChangesetParser {
|
2011-01-25 00:52:35 +01:00
|
|
|
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02:00
|
|
|
const HIGHLIGHT_BYTE_LIMIT = 262144;
|
|
|
|
|
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;
|
2013-01-11 01:06:39 +01:00
|
|
|
protected $hunkStartLines = array();
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
protected $comments = array();
|
|
|
|
protected $specialAttributes = array();
|
|
|
|
|
|
|
|
protected $changeset;
|
|
|
|
protected $whitespaceMode = null;
|
|
|
|
|
2011-05-05 16:08:10 +02:00
|
|
|
protected $renderCacheKey = null;
|
|
|
|
|
2012-12-08 03:08:27 +01:00
|
|
|
private $handles = array();
|
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;
|
|
|
|
|
2012-06-15 09:53:26 +02:00
|
|
|
private $originalLeft;
|
|
|
|
private $originalRight;
|
|
|
|
|
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-20 22:49:17 +02:00
|
|
|
private $isTopLevel;
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02: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
|
|
|
private $coverage;
|
2012-03-20 03:17:59 +01:00
|
|
|
private $markupEngine;
|
2012-04-13 08:52:50 +02:00
|
|
|
private $highlightErrors;
|
Implement basic one-up and test renderers
Summary:
This is a half-step toward one-up and test renderers. This is mostly a structural change, and has no user-facing impact. It splits the rendering hierarchy like this:
- Renderer (more methods are abstract than before)
- HTML Renderer (most HTML stuff has moved down from the base to here)
- HTML 1-up (placeholder only -- not yet a functional implementation)
- HTML 2-up (minimal changes, uses mostly old code)
- Test Renderer (unit-testable renderer base, implements text versions of the HTML stuff)
- Test 1-up (selects 1-up mode for test rendering)
- Test 2-up (selects 2-up mode for test rendering)
Broadly, I'm trying to share as much code as possible by splitting rendering into more, smaller stages. Specifically, we do this:
- Combine the various sorts of inputs (changes, context, inlines, etc.) into a single, relatively homogenous list of "primitives". This happens in the base class.
- The primitive types are: old (diff left side), new (diff right side), context ("show more context"), no-context ("context not available") and inline (inline comment).
- Possibly, apply a filtering/reordering step to the primitives to get them ready for 1-up rendering. This mostly removes information, and does a small amount of reordering. This also happens in the base class.
- Pass the primitives to the actual renderer, to convert them into HTML, text, or whatever else. This happens in the leaf class.
The primitive implementation is not yet complete (it doesn't attach as much information to the primitives as it should -- stuff like coverage and copies), but covers the basics.
The existing HTMLTwoUp renderer does not use the primitive path; instead, it still goes down the old path.
Test Plan: Ran unit tests, looked at a bunch of diffs.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4421
2013-01-14 23:20:06 +01:00
|
|
|
private $disableCache;
|
|
|
|
private $renderer;
|
2014-06-20 20:49:41 +02:00
|
|
|
private $characterEncoding;
|
|
|
|
private $highlightAs;
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02:00
|
|
|
private $highlightingDisabled;
|
2015-01-30 20:17:34 +01:00
|
|
|
private $showEditAndReplyLinks = true;
|
Track a "Done" state on inline comments
Summary:
Ref T1460. This just barely works, but throwing it up in case any of it sounds mechanically crazy before we build integrations/UI/etc.
Specifically, these are the behaviors:
- You can mark your own draft comments as "done" before you submit them. The intent is to let reviewers mark their stuff advisory/minor/not-important before they submit it, to hint to authors that they don't expect the feedback to necessarily be addressed (maybe it's a joke, maybe it's just discussion, maybe it's "consider..").
- You can mark others' published comments as "done" if you're the revision/commit author. The intent is to keep this lightweight by not requiring an audit trail of who marked what done when. If anyone could mark anything done, we'd have to have some way to show who marked stuff.
- When you mark stuff done (or unmark it), it goes into a "draft" state, where you see the change but others don't see it yet. The intent is twofold:
- Be consistent with how inlines work.
- Allow us to publish a "epriestley updated this revision + epriestley marked 15 inlines as done" story later if we want. This seems more useful than publishing 15 "epriestley marked one thing as done" stories.
- The actual bit where done-ness publishes isn't implemented.
- UI is bare bones.
- No integration with the rest of the UI yet.
Test Plan: Clicked some checkboxes.
Reviewers: chad, btrahan
Reviewed By: btrahan
Subscribers: paulshen, chasemp, epriestley
Maniphest Tasks: T1460
Differential Revision: https://secure.phabricator.com/D12033
2015-03-10 02:41:47 +01:00
|
|
|
private $canMarkDone;
|
2015-03-27 19:23:10 +01:00
|
|
|
private $objectOwnerPHID;
|
2015-01-30 20:17:34 +01:00
|
|
|
|
Use ChangesetListView on Differential standalone view
Summary:
Fixes T4452. Ref T2009. There's a hierarchy of changeset rendering power: only low-level calls, use of ChangesetDetailView, then use of ChangesetListView (a list of DetailViews).
Prior to work here, the various changeset rendering controllers got their hands dirty to varying degrees, with some using only the lowest-level rendering pipeline:
- Phriction: no view (lowest level)
- Diffusion: DetailView
- Differential Changeset: DetailView
- Differential Diff: ListView
- Differential Revision: ListView
I brought Phriction up to use DetailView, but want to bring everything all the way up to use ListView. Each composition layer adds more features to diff browsing. In particular, this change enables "Highlight As", switching 1up vs 2up, adding inlines, etc., on the standalone view.
Test Plan:
- Viewed a changeset standalone. Could change highlighting, switch 1up vs 2up, add and edit inlines, etc.
- Viewed a revision; no behavioral changes.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T4452, T2009
Differential Revision: https://secure.phabricator.com/D12012
2015-03-08 12:10:11 +01:00
|
|
|
private $rangeStart;
|
|
|
|
private $rangeEnd;
|
|
|
|
private $mask;
|
|
|
|
|
|
|
|
public function setRange($start, $end) {
|
|
|
|
$this->rangeStart = $start;
|
|
|
|
$this->rangeEnd = $end;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setMask(array $mask) {
|
|
|
|
$this->mask = $mask;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function renderChangeset() {
|
|
|
|
return $this->render($this->rangeStart, $this->rangeEnd, $this->mask);
|
|
|
|
}
|
|
|
|
|
2015-01-30 20:17:34 +01:00
|
|
|
public function setShowEditAndReplyLinks($bool) {
|
|
|
|
$this->showEditAndReplyLinks = $bool;
|
|
|
|
return $this;
|
|
|
|
}
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02:00
|
|
|
|
2015-01-30 20:17:34 +01:00
|
|
|
public function getShowEditAndReplyLinks() {
|
|
|
|
return $this->showEditAndReplyLinks;
|
|
|
|
}
|
2014-06-20 20:49:41 +02:00
|
|
|
|
|
|
|
public function setHighlightAs($highlight_as) {
|
|
|
|
$this->highlightAs = $highlight_as;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getHighlightAs() {
|
|
|
|
return $this->highlightAs;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setCharacterEncoding($character_encoding) {
|
|
|
|
$this->characterEncoding = $character_encoding;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCharacterEncoding() {
|
|
|
|
return $this->characterEncoding;
|
|
|
|
}
|
Implement basic one-up and test renderers
Summary:
This is a half-step toward one-up and test renderers. This is mostly a structural change, and has no user-facing impact. It splits the rendering hierarchy like this:
- Renderer (more methods are abstract than before)
- HTML Renderer (most HTML stuff has moved down from the base to here)
- HTML 1-up (placeholder only -- not yet a functional implementation)
- HTML 2-up (minimal changes, uses mostly old code)
- Test Renderer (unit-testable renderer base, implements text versions of the HTML stuff)
- Test 1-up (selects 1-up mode for test rendering)
- Test 2-up (selects 2-up mode for test rendering)
Broadly, I'm trying to share as much code as possible by splitting rendering into more, smaller stages. Specifically, we do this:
- Combine the various sorts of inputs (changes, context, inlines, etc.) into a single, relatively homogenous list of "primitives". This happens in the base class.
- The primitive types are: old (diff left side), new (diff right side), context ("show more context"), no-context ("context not available") and inline (inline comment).
- Possibly, apply a filtering/reordering step to the primitives to get them ready for 1-up rendering. This mostly removes information, and does a small amount of reordering. This also happens in the base class.
- Pass the primitives to the actual renderer, to convert them into HTML, text, or whatever else. This happens in the leaf class.
The primitive implementation is not yet complete (it doesn't attach as much information to the primitives as it should -- stuff like coverage and copies), but covers the basics.
The existing HTMLTwoUp renderer does not use the primitive path; instead, it still goes down the old path.
Test Plan: Ran unit tests, looked at a bunch of diffs.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4421
2013-01-14 23:20:06 +01:00
|
|
|
|
2015-01-30 20:17:34 +01:00
|
|
|
public function setRenderer(DifferentialChangesetRenderer $renderer) {
|
Implement basic one-up and test renderers
Summary:
This is a half-step toward one-up and test renderers. This is mostly a structural change, and has no user-facing impact. It splits the rendering hierarchy like this:
- Renderer (more methods are abstract than before)
- HTML Renderer (most HTML stuff has moved down from the base to here)
- HTML 1-up (placeholder only -- not yet a functional implementation)
- HTML 2-up (minimal changes, uses mostly old code)
- Test Renderer (unit-testable renderer base, implements text versions of the HTML stuff)
- Test 1-up (selects 1-up mode for test rendering)
- Test 2-up (selects 2-up mode for test rendering)
Broadly, I'm trying to share as much code as possible by splitting rendering into more, smaller stages. Specifically, we do this:
- Combine the various sorts of inputs (changes, context, inlines, etc.) into a single, relatively homogenous list of "primitives". This happens in the base class.
- The primitive types are: old (diff left side), new (diff right side), context ("show more context"), no-context ("context not available") and inline (inline comment).
- Possibly, apply a filtering/reordering step to the primitives to get them ready for 1-up rendering. This mostly removes information, and does a small amount of reordering. This also happens in the base class.
- Pass the primitives to the actual renderer, to convert them into HTML, text, or whatever else. This happens in the leaf class.
The primitive implementation is not yet complete (it doesn't attach as much information to the primitives as it should -- stuff like coverage and copies), but covers the basics.
The existing HTMLTwoUp renderer does not use the primitive path; instead, it still goes down the old path.
Test Plan: Ran unit tests, looked at a bunch of diffs.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4421
2013-01-14 23:20:06 +01:00
|
|
|
$this->renderer = $renderer;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getRenderer() {
|
|
|
|
if (!$this->renderer) {
|
|
|
|
return new DifferentialChangesetTwoUpRenderer();
|
|
|
|
}
|
|
|
|
return $this->renderer;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setDisableCache($disable_cache) {
|
|
|
|
$this->disableCache = $disable_cache;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getDisableCache() {
|
|
|
|
return $this->disableCache;
|
|
|
|
}
|
2011-06-08 21:39:03 +02:00
|
|
|
|
Track a "Done" state on inline comments
Summary:
Ref T1460. This just barely works, but throwing it up in case any of it sounds mechanically crazy before we build integrations/UI/etc.
Specifically, these are the behaviors:
- You can mark your own draft comments as "done" before you submit them. The intent is to let reviewers mark their stuff advisory/minor/not-important before they submit it, to hint to authors that they don't expect the feedback to necessarily be addressed (maybe it's a joke, maybe it's just discussion, maybe it's "consider..").
- You can mark others' published comments as "done" if you're the revision/commit author. The intent is to keep this lightweight by not requiring an audit trail of who marked what done when. If anyone could mark anything done, we'd have to have some way to show who marked stuff.
- When you mark stuff done (or unmark it), it goes into a "draft" state, where you see the change but others don't see it yet. The intent is twofold:
- Be consistent with how inlines work.
- Allow us to publish a "epriestley updated this revision + epriestley marked 15 inlines as done" story later if we want. This seems more useful than publishing 15 "epriestley marked one thing as done" stories.
- The actual bit where done-ness publishes isn't implemented.
- UI is bare bones.
- No integration with the rest of the UI yet.
Test Plan: Clicked some checkboxes.
Reviewers: chad, btrahan
Reviewed By: btrahan
Subscribers: paulshen, chasemp, epriestley
Maniphest Tasks: T1460
Differential Revision: https://secure.phabricator.com/D12033
2015-03-10 02:41:47 +01:00
|
|
|
public function setCanMarkDone($can_mark_done) {
|
|
|
|
$this->canMarkDone = $can_mark_done;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getCanMarkDone() {
|
|
|
|
return $this->canMarkDone;
|
|
|
|
}
|
|
|
|
|
2015-03-27 19:23:10 +01:00
|
|
|
public function setObjectOwnerPHID($phid) {
|
|
|
|
$this->objectOwnerPHID = $phid;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getObjectOwnerPHID() {
|
|
|
|
return $this->objectOwnerPHID;
|
|
|
|
}
|
|
|
|
|
Make "Show Context" persist rendering, whitespace, encoding, etc
Summary:
Ref T2009. Currently, we do not persist view parameters when making context rendering requests.
The big one is the renderer (1up vs 2up). This makes context on unified diffs come in with too many columns.
However, it impacts other parameters too. For example, at HEAD, if you change highlighting to "rainbow" and then load more context, the context uses the original highlighter instead of the rainbow highlighter.
This moves context loads into ChangesetViewManager, which maintains view parameters and can provide them correctly.
- This removes "ref"; it is no longer required, as the ChangesetViewManager tracks it.
- This removes URI management from `behavior-show-more`; it is no longer required, since the ChangesetViewManager knows how to render.
- This removes "whitespace" since this is handled properly by the view manager.
Test Plan:
- Used "Show Top" / "Show All" / "Show Bottom" in 1-up and 2-up views.
- Changed file highlighting to rainbow, loaded stuff, saw rainbow stick.
- Used "Show Entire File" in 1-up and 2-up views.
- Saw loading chrome.
- No loading chrome normally.
- Made inlines, verified `copyRows()` code runs.
- Poked around Diffusion -- it is missing some parameter handling, but works OK.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D11977
2015-03-05 23:03:00 +01:00
|
|
|
public static function getDefaultRendererForViewer(PhabricatorUser $viewer) {
|
|
|
|
$prefs = $viewer->loadPreferences();
|
|
|
|
$pref_unified = PhabricatorUserPreferences::PREFERENCE_DIFF_UNIFIED;
|
|
|
|
if ($prefs->getPreference($pref_unified) == 'unified') {
|
|
|
|
return '1up';
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function readParametersFromRequest(AphrontRequest $request) {
|
|
|
|
$this->setWhitespaceMode($request->getStr('whitespace'));
|
|
|
|
$this->setCharacterEncoding($request->getStr('encoding'));
|
|
|
|
$this->setHighlightAs($request->getStr('highlight'));
|
|
|
|
|
|
|
|
$renderer = null;
|
|
|
|
|
|
|
|
// If the viewer prefers unified diffs, always set the renderer to unified.
|
|
|
|
// Otherwise, we leave it unspecified and the client will choose a
|
|
|
|
// renderer based on the screen size.
|
|
|
|
|
|
|
|
if ($request->getStr('renderer')) {
|
|
|
|
$renderer = $request->getStr('renderer');
|
|
|
|
} else {
|
|
|
|
$renderer = self::getDefaultRendererForViewer($request->getViewer());
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($renderer) {
|
|
|
|
case '1up':
|
|
|
|
$this->setRenderer(new DifferentialChangesetOneUpRenderer());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
$this->setRenderer(new DifferentialChangesetTwoUpRenderer());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-02-13 23:45:57 +01:00
|
|
|
const CACHE_VERSION = 11;
|
2012-08-27 22:08:58 +02:00
|
|
|
const CACHE_MAX_SIZE = 8e6;
|
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';
|
2014-11-18 00:12:07 +01:00
|
|
|
const ATTR_MOVEAWAY = 'attr:moveaway';
|
2011-01-25 00:52:35 +01:00
|
|
|
|
|
|
|
const LINES_CONTEXT = 8;
|
|
|
|
|
|
|
|
const WHITESPACE_SHOW_ALL = 'show-all';
|
|
|
|
const WHITESPACE_IGNORE_TRAILING = 'ignore-trailing';
|
2015-02-10 08:34:43 +01:00
|
|
|
const WHITESPACE_IGNORE_MOST = 'ignore-most';
|
2015-02-10 20:52:56 +01:00
|
|
|
const WHITESPACE_IGNORE_ALL = 'ignore-all';
|
2012-03-13 01:06:36 +01:00
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
public function setOldLines(array $lines) {
|
|
|
|
$this->old = $lines;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setNewLines(array $lines) {
|
|
|
|
$this->new = $lines;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setSpecialAttributes(array $attributes) {
|
|
|
|
$this->specialAttributes = $attributes;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setIntraLineDiffs(array $diffs) {
|
|
|
|
$this->intra = $diffs;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setVisibileLinesMask(array $mask) {
|
|
|
|
$this->visible = $mask;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2012-06-15 09:53:26 +02:00
|
|
|
public function setOriginals(
|
|
|
|
DifferentialChangeset $left,
|
|
|
|
DifferentialChangeset $right) {
|
|
|
|
|
|
|
|
$this->originalLeft = $left;
|
|
|
|
$this->originalRight = $right;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function diffOriginals() {
|
|
|
|
$engine = new PhabricatorDifferenceEngine();
|
|
|
|
$changeset = $engine->generateChangesetFromFileContent(
|
|
|
|
implode('', mpull($this->originalLeft->getHunks(), 'getChanges')),
|
|
|
|
implode('', mpull($this->originalRight->getHunks(), 'getChanges')));
|
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
$parser = new DifferentialHunkParser();
|
2012-06-15 09:53:26 +02:00
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
return $parser->parseHunksForHighlightMasks(
|
|
|
|
$changeset->getHunks(),
|
|
|
|
$this->originalLeft->getHunks(),
|
2013-02-19 22:33:10 +01:00
|
|
|
$this->originalRight->getHunks());
|
2012-06-15 09:53:26 +02: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getRenderCacheKey() {
|
|
|
|
return $this->renderCacheKey;
|
|
|
|
}
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
public function setChangeset(DifferentialChangeset $changeset) {
|
2011-01-25 00:52:35 +01:00
|
|
|
$this->changeset = $changeset;
|
2011-06-08 21:39:03 +02:00
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
$this->setFilename($changeset->getFilename());
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
private function getRenderingReference() {
|
|
|
|
return $this->renderingReference;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2012-10-24 02:33:58 +02:00
|
|
|
public function setMarkupEngine(PhabricatorMarkupEngine $engine) {
|
2011-02-02 01:42:36 +01:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
2015-01-30 20:17:34 +01:00
|
|
|
public function getUser() {
|
|
|
|
return $this->user;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
2012-12-12 02:16:11 +01:00
|
|
|
private function getCoverage() {
|
|
|
|
return $this->coverage;
|
|
|
|
}
|
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
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
private 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;
|
|
|
|
}
|
|
|
|
|
2013-02-13 23:45:57 +01:00
|
|
|
if ($data['cache'][0] == '{') {
|
|
|
|
// This is likely an old-style JSON cache which we will not be able to
|
|
|
|
// deserialize.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$data = unserialize($data['cache']);
|
2011-01-25 00:52:35 +01:00
|
|
|
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',
|
2013-01-11 01:06:39 +01:00
|
|
|
'hunkStartLines',
|
2011-01-25 00:52:35 +01:00
|
|
|
'cacheVersion',
|
|
|
|
'cacheHost',
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02:00
|
|
|
'highlightingDisabled',
|
2011-01-25 00:52:35 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2013-02-13 23:45:57 +01:00
|
|
|
$cache = serialize($cache);
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2012-08-27 22:08:58 +02:00
|
|
|
// We don't want to waste too much space by a single changeset.
|
|
|
|
if (strlen($cache) > self::CACHE_MAX_SIZE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-05-18 01:34:13 +02:00
|
|
|
$changeset = new DifferentialChangeset();
|
|
|
|
$conn_w = $changeset->establishConnection('w');
|
2011-08-16 23:44:13 +02:00
|
|
|
|
2014-05-18 01:34:13 +02:00
|
|
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
Allow Herald to "Require legal signatures" for reviews
Summary:
Ref T3116. Add a Herald action "Require legal signatures" which requires revision authors to accept legal agreements before their revisions can be accepted.
- Herald will check which documents the author has signed, and trigger a "you have to sign X, Y, Z" for other documents.
- If the author has already signed everything, we don't spam the revision -- basically, this only triggers when signatures are missing.
- The UI will show which documents must be signed and warn that the revision can't be accepted until they're completed.
- Users aren't allowed to "Accept" the revision until documents are cleared.
Fixes T1157. The original install making the request (Hive) no longer uses Phabricator, and this satisfies our requirements.
Test Plan:
- Added a Herald rule.
- Created a revision, saw the rule trigger.
- Viewed as author and non-author, saw field UI (generic for non-author, specific for author), transaction UI, and accept-warning UI.
- Tried to accept revision.
- Signed document, saw UI update. Note that signatures don't currently //push// an update to the revision, but could eventually (like blocking tasks work).
- Accepted revision.
- Created another revision, saw rules not add the document (since it's already signed, this is the "no spam" case).
Reviewers: btrahan, chad
Reviewed By: chad
Subscribers: asherkin, epriestley
Maniphest Tasks: T1157, T3116
Differential Revision: https://secure.phabricator.com/D9771
2014-06-29 16:53:53 +02:00
|
|
|
try {
|
|
|
|
queryfx(
|
|
|
|
$conn_w,
|
|
|
|
'INSERT INTO %T (id, cache, dateCreated) VALUES (%d, %B, %d)
|
|
|
|
ON DUPLICATE KEY UPDATE cache = VALUES(cache)',
|
|
|
|
DifferentialChangeset::TABLE_CACHE,
|
|
|
|
$render_cache_key,
|
|
|
|
$cache,
|
|
|
|
time());
|
|
|
|
} catch (AphrontQueryException $ex) {
|
|
|
|
// Ignore these exceptions. A common cause is that the cache is
|
|
|
|
// larger than 'max_allowed_packet', in which case we're better off
|
|
|
|
// not writing it.
|
|
|
|
|
|
|
|
// TODO: It would be nice to tailor this more narrowly.
|
|
|
|
}
|
2014-05-18 01:34:13 +02:00
|
|
|
unset($unguarded);
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2012-10-01 23:17:13 +02:00
|
|
|
private function markGenerated($new_corpus_block = '') {
|
|
|
|
$generated_guess = (strpos($new_corpus_block, '@'.'generated') !== false);
|
|
|
|
|
|
|
|
if (!$generated_guess) {
|
2013-01-17 01:57:09 +01:00
|
|
|
$generated_path_regexps = PhabricatorEnv::getEnvConfig(
|
2013-01-19 21:11:11 +01:00
|
|
|
'differential.generated-paths');
|
2012-10-01 23:17:13 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-11-18 00:12:07 +01:00
|
|
|
public function isMoveAway() {
|
|
|
|
return idx($this->specialAttributes, self::ATTR_MOVEAWAY, false);
|
|
|
|
}
|
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
private function applyIntraline(&$render, $intra, $corpus) {
|
2012-01-17 18:42:30 +01:00
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
foreach ($render as $key => $text) {
|
|
|
|
if (isset($intra[$key])) {
|
|
|
|
$render[$key] = ArcanistDiffUtils::applyIntralineDiff(
|
|
|
|
$text,
|
|
|
|
$intra[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
private function getHighlightFuture($corpus) {
|
2014-06-20 20:49:41 +02:00
|
|
|
$language = $this->highlightAs;
|
|
|
|
|
|
|
|
if (!$language) {
|
|
|
|
$language = $this->highlightEngine->getLanguageFromFilename(
|
|
|
|
$this->filename);
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02:00
|
|
|
|
|
|
|
if (($language != 'txt') &&
|
|
|
|
(strlen($corpus) > self::HIGHLIGHT_BYTE_LIMIT)) {
|
|
|
|
$this->highlightingDisabled = true;
|
|
|
|
$language = 'txt';
|
|
|
|
}
|
2014-06-20 20:49:41 +02:00
|
|
|
}
|
|
|
|
|
2011-05-21 06:15:00 +02:00
|
|
|
return $this->highlightEngine->getHighlightFuture(
|
2014-06-20 20:49:41 +02:00
|
|
|
$language,
|
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
|
|
|
|
2012-10-20 16:11:19 +02:00
|
|
|
$result_lines = phutil_split_lines($result);
|
2011-01-25 00:52:35 +01:00
|
|
|
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:
|
2015-02-10 20:52:56 +01:00
|
|
|
case self::WHITESPACE_IGNORE_ALL:
|
2011-01-25 00:52:35 +01:00
|
|
|
break;
|
|
|
|
default:
|
2015-02-10 08:34:43 +01:00
|
|
|
$whitespace_mode = self::WHITESPACE_IGNORE_MOST;
|
2011-01-25 00:52:35 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-10 08:34:43 +01:00
|
|
|
$skip_cache = ($whitespace_mode != self::WHITESPACE_IGNORE_MOST);
|
Implement basic one-up and test renderers
Summary:
This is a half-step toward one-up and test renderers. This is mostly a structural change, and has no user-facing impact. It splits the rendering hierarchy like this:
- Renderer (more methods are abstract than before)
- HTML Renderer (most HTML stuff has moved down from the base to here)
- HTML 1-up (placeholder only -- not yet a functional implementation)
- HTML 2-up (minimal changes, uses mostly old code)
- Test Renderer (unit-testable renderer base, implements text versions of the HTML stuff)
- Test 1-up (selects 1-up mode for test rendering)
- Test 2-up (selects 2-up mode for test rendering)
Broadly, I'm trying to share as much code as possible by splitting rendering into more, smaller stages. Specifically, we do this:
- Combine the various sorts of inputs (changes, context, inlines, etc.) into a single, relatively homogenous list of "primitives". This happens in the base class.
- The primitive types are: old (diff left side), new (diff right side), context ("show more context"), no-context ("context not available") and inline (inline comment).
- Possibly, apply a filtering/reordering step to the primitives to get them ready for 1-up rendering. This mostly removes information, and does a small amount of reordering. This also happens in the base class.
- Pass the primitives to the actual renderer, to convert them into HTML, text, or whatever else. This happens in the leaf class.
The primitive implementation is not yet complete (it doesn't attach as much information to the primitives as it should -- stuff like coverage and copies), but covers the basics.
The existing HTMLTwoUp renderer does not use the primitive path; instead, it still goes down the old path.
Test Plan: Ran unit tests, looked at a bunch of diffs.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4421
2013-01-14 23:20:06 +01:00
|
|
|
if ($this->disableCache) {
|
|
|
|
$skip_cache = true;
|
|
|
|
}
|
|
|
|
|
2014-06-20 20:49:41 +02:00
|
|
|
if ($this->characterEncoding) {
|
|
|
|
$skip_cache = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->highlightAs) {
|
|
|
|
$skip_cache = true;
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
$this->whitespaceMode = $whitespace_mode;
|
|
|
|
|
|
|
|
$changeset = $this->changeset;
|
|
|
|
|
2012-10-01 23:17:13 +02:00
|
|
|
if ($changeset->getFileType() != DifferentialChangeType::FILE_TEXT &&
|
|
|
|
$changeset->getFileType() != DifferentialChangeType::FILE_SYMLINK) {
|
2013-01-09 22:11:17 +01:00
|
|
|
|
2012-10-01 23:17:13 +02:00
|
|
|
$this->markGenerated();
|
|
|
|
|
|
|
|
} else {
|
2011-01-25 00:52:35 +01:00
|
|
|
if ($skip_cache || !$this->loadCache()) {
|
2013-01-09 22:11:17 +01:00
|
|
|
$this->process();
|
|
|
|
if (!$skip_cache) {
|
|
|
|
$this->saveCache();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
private function process() {
|
|
|
|
$whitespace_mode = $this->whitespaceMode;
|
|
|
|
$changeset = $this->changeset;
|
2012-03-13 01:06:36 +01:00
|
|
|
|
2015-02-10 08:34:43 +01:00
|
|
|
$ignore_all = (($whitespace_mode == self::WHITESPACE_IGNORE_MOST) ||
|
2015-02-10 20:52:56 +01:00
|
|
|
($whitespace_mode == self::WHITESPACE_IGNORE_ALL));
|
2011-06-29 01:21:06 +02:00
|
|
|
|
2015-02-10 20:52:56 +01:00
|
|
|
$force_ignore = ($whitespace_mode == self::WHITESPACE_IGNORE_ALL);
|
2011-07-22 22:15:11 +02:00
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
if (!$force_ignore) {
|
|
|
|
if ($ignore_all && $changeset->getWhitespaceMatters()) {
|
|
|
|
$ignore_all = false;
|
|
|
|
}
|
|
|
|
}
|
2011-06-29 01:21:06 +02:00
|
|
|
|
2013-01-09 22:11:17 +01: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.
|
|
|
|
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) {
|
2011-06-29 01:21:06 +02:00
|
|
|
$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
|
|
|
}
|
2013-01-09 22:11:17 +01: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
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Simplify hunk parsing
Summary:
Simplify whitespace-ignoring diffing:
- Currently, we generate two diffs (one ignoring whitespace, one normal) and then copy the //text content// from the normal one to the whitespace-ignoring one.
- Instead, copy the //change types// from the ignoring one to the normal one.
- This is cheaper and much simpler.
- It means that we have the right change types in reparseHunksForSpecialAttributes(), and can simplify some other things.
Simplify whitespace changes, unchanged files, and deleted file detections:
- Currently, we do this inline with a bunch of other stuff, in the reparse step.
- Instead, do it separately. This is simpler.
Simplify intraline whitespace handling:
- Currently, this is a complicated mess that makes roughly zero sense.
- Instead, do it in reparse in a straightforward way.
Partially fix handling of files changed only by changing whitespace.
Partially fix handling of unchanged files.
Test Plan:
- Ran unit tests.
- Created context-less diffs, verified they rendered reasonably.
- Generated a diff with prefix whitespace, suffix whitespace, intraline whitespace, and non-whitespace changes. Verified changes rendered correctly in "ignore most" and "show all" modes.
- Verified unchanged files and files with only whitspace changes render with the correct masks.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4398
2013-01-12 00:25:15 +01:00
|
|
|
$hunk_parser = new DifferentialHunkParser();
|
|
|
|
$hunk_parser->setWhitespaceMode($whitespace_mode);
|
|
|
|
$hunk_parser->parseHunksForLineData($changeset->getHunks());
|
2013-01-09 22:11:17 +01:00
|
|
|
|
Simplify hunk parsing
Summary:
Simplify whitespace-ignoring diffing:
- Currently, we generate two diffs (one ignoring whitespace, one normal) and then copy the //text content// from the normal one to the whitespace-ignoring one.
- Instead, copy the //change types// from the ignoring one to the normal one.
- This is cheaper and much simpler.
- It means that we have the right change types in reparseHunksForSpecialAttributes(), and can simplify some other things.
Simplify whitespace changes, unchanged files, and deleted file detections:
- Currently, we do this inline with a bunch of other stuff, in the reparse step.
- Instead, do it separately. This is simpler.
Simplify intraline whitespace handling:
- Currently, this is a complicated mess that makes roughly zero sense.
- Instead, do it in reparse in a straightforward way.
Partially fix handling of files changed only by changing whitespace.
Partially fix handling of unchanged files.
Test Plan:
- Ran unit tests.
- Created context-less diffs, verified they rendered reasonably.
- Generated a diff with prefix whitespace, suffix whitespace, intraline whitespace, and non-whitespace changes. Verified changes rendered correctly in "ignore most" and "show all" modes.
- Verified unchanged files and files with only whitspace changes render with the correct masks.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4398
2013-01-12 00:25:15 +01:00
|
|
|
// Depending on the whitespace mode, we may need to compute a different
|
|
|
|
// set of changes than the set of changes in the hunk data (specificaly,
|
|
|
|
// we might want to consider changed lines which have only whitespace
|
|
|
|
// changes as unchanged).
|
|
|
|
if ($ignore_all) {
|
2013-01-09 22:11:17 +01:00
|
|
|
$engine = new PhabricatorDifferenceEngine();
|
|
|
|
$engine->setIgnoreWhitespace(true);
|
|
|
|
$no_whitespace_changeset = $engine->generateChangesetFromFileContent(
|
|
|
|
$old_file,
|
|
|
|
$new_file);
|
|
|
|
|
Simplify hunk parsing
Summary:
Simplify whitespace-ignoring diffing:
- Currently, we generate two diffs (one ignoring whitespace, one normal) and then copy the //text content// from the normal one to the whitespace-ignoring one.
- Instead, copy the //change types// from the ignoring one to the normal one.
- This is cheaper and much simpler.
- It means that we have the right change types in reparseHunksForSpecialAttributes(), and can simplify some other things.
Simplify whitespace changes, unchanged files, and deleted file detections:
- Currently, we do this inline with a bunch of other stuff, in the reparse step.
- Instead, do it separately. This is simpler.
Simplify intraline whitespace handling:
- Currently, this is a complicated mess that makes roughly zero sense.
- Instead, do it in reparse in a straightforward way.
Partially fix handling of files changed only by changing whitespace.
Partially fix handling of unchanged files.
Test Plan:
- Ran unit tests.
- Created context-less diffs, verified they rendered reasonably.
- Generated a diff with prefix whitespace, suffix whitespace, intraline whitespace, and non-whitespace changes. Verified changes rendered correctly in "ignore most" and "show all" modes.
- Verified unchanged files and files with only whitspace changes render with the correct masks.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4398
2013-01-12 00:25:15 +01:00
|
|
|
$type_parser = new DifferentialHunkParser();
|
|
|
|
$type_parser->parseHunksForLineData($no_whitespace_changeset->getHunks());
|
|
|
|
|
|
|
|
$hunk_parser->setOldLineTypeMap($type_parser->getOldLineTypeMap());
|
|
|
|
$hunk_parser->setNewLineTypeMap($type_parser->getNewLineTypeMap());
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$hunk_parser->reparseHunksForSpecialAttributes();
|
|
|
|
|
|
|
|
$unchanged = false;
|
Simplify hunk parsing
Summary:
Simplify whitespace-ignoring diffing:
- Currently, we generate two diffs (one ignoring whitespace, one normal) and then copy the //text content// from the normal one to the whitespace-ignoring one.
- Instead, copy the //change types// from the ignoring one to the normal one.
- This is cheaper and much simpler.
- It means that we have the right change types in reparseHunksForSpecialAttributes(), and can simplify some other things.
Simplify whitespace changes, unchanged files, and deleted file detections:
- Currently, we do this inline with a bunch of other stuff, in the reparse step.
- Instead, do it separately. This is simpler.
Simplify intraline whitespace handling:
- Currently, this is a complicated mess that makes roughly zero sense.
- Instead, do it in reparse in a straightforward way.
Partially fix handling of files changed only by changing whitespace.
Partially fix handling of unchanged files.
Test Plan:
- Ran unit tests.
- Created context-less diffs, verified they rendered reasonably.
- Generated a diff with prefix whitespace, suffix whitespace, intraline whitespace, and non-whitespace changes. Verified changes rendered correctly in "ignore most" and "show all" modes.
- Verified unchanged files and files with only whitspace changes render with the correct masks.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4398
2013-01-12 00:25:15 +01:00
|
|
|
if (!$hunk_parser->getHasAnyChanges()) {
|
|
|
|
$filetype = $this->changeset->getFileType();
|
|
|
|
if ($filetype == DifferentialChangeType::FILE_TEXT ||
|
|
|
|
$filetype == DifferentialChangeType::FILE_SYMLINK) {
|
|
|
|
$unchanged = true;
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
|
|
|
}
|
Simplify hunk parsing
Summary:
Simplify whitespace-ignoring diffing:
- Currently, we generate two diffs (one ignoring whitespace, one normal) and then copy the //text content// from the normal one to the whitespace-ignoring one.
- Instead, copy the //change types// from the ignoring one to the normal one.
- This is cheaper and much simpler.
- It means that we have the right change types in reparseHunksForSpecialAttributes(), and can simplify some other things.
Simplify whitespace changes, unchanged files, and deleted file detections:
- Currently, we do this inline with a bunch of other stuff, in the reparse step.
- Instead, do it separately. This is simpler.
Simplify intraline whitespace handling:
- Currently, this is a complicated mess that makes roughly zero sense.
- Instead, do it in reparse in a straightforward way.
Partially fix handling of files changed only by changing whitespace.
Partially fix handling of unchanged files.
Test Plan:
- Ran unit tests.
- Created context-less diffs, verified they rendered reasonably.
- Generated a diff with prefix whitespace, suffix whitespace, intraline whitespace, and non-whitespace changes. Verified changes rendered correctly in "ignore most" and "show all" modes.
- Verified unchanged files and files with only whitspace changes render with the correct masks.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4398
2013-01-12 00:25:15 +01:00
|
|
|
|
2014-11-18 00:12:07 +01:00
|
|
|
$moveaway = false;
|
2013-01-09 22:11:17 +01:00
|
|
|
$changetype = $this->changeset->getChangeType();
|
|
|
|
if ($changetype == DifferentialChangeType::TYPE_MOVE_AWAY) {
|
2014-11-18 00:12:07 +01:00
|
|
|
$moveaway = true;
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
Simplify hunk parsing
Summary:
Simplify whitespace-ignoring diffing:
- Currently, we generate two diffs (one ignoring whitespace, one normal) and then copy the //text content// from the normal one to the whitespace-ignoring one.
- Instead, copy the //change types// from the ignoring one to the normal one.
- This is cheaper and much simpler.
- It means that we have the right change types in reparseHunksForSpecialAttributes(), and can simplify some other things.
Simplify whitespace changes, unchanged files, and deleted file detections:
- Currently, we do this inline with a bunch of other stuff, in the reparse step.
- Instead, do it separately. This is simpler.
Simplify intraline whitespace handling:
- Currently, this is a complicated mess that makes roughly zero sense.
- Instead, do it in reparse in a straightforward way.
Partially fix handling of files changed only by changing whitespace.
Partially fix handling of unchanged files.
Test Plan:
- Ran unit tests.
- Created context-less diffs, verified they rendered reasonably.
- Generated a diff with prefix whitespace, suffix whitespace, intraline whitespace, and non-whitespace changes. Verified changes rendered correctly in "ignore most" and "show all" modes.
- Verified unchanged files and files with only whitspace changes render with the correct masks.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4398
2013-01-12 00:25:15 +01:00
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
$this->setSpecialAttributes(array(
|
|
|
|
self::ATTR_UNCHANGED => $unchanged,
|
|
|
|
self::ATTR_DELETED => $hunk_parser->getIsDeleted(),
|
Simplify hunk parsing
Summary:
Simplify whitespace-ignoring diffing:
- Currently, we generate two diffs (one ignoring whitespace, one normal) and then copy the //text content// from the normal one to the whitespace-ignoring one.
- Instead, copy the //change types// from the ignoring one to the normal one.
- This is cheaper and much simpler.
- It means that we have the right change types in reparseHunksForSpecialAttributes(), and can simplify some other things.
Simplify whitespace changes, unchanged files, and deleted file detections:
- Currently, we do this inline with a bunch of other stuff, in the reparse step.
- Instead, do it separately. This is simpler.
Simplify intraline whitespace handling:
- Currently, this is a complicated mess that makes roughly zero sense.
- Instead, do it in reparse in a straightforward way.
Partially fix handling of files changed only by changing whitespace.
Partially fix handling of unchanged files.
Test Plan:
- Ran unit tests.
- Created context-less diffs, verified they rendered reasonably.
- Generated a diff with prefix whitespace, suffix whitespace, intraline whitespace, and non-whitespace changes. Verified changes rendered correctly in "ignore most" and "show all" modes.
- Verified unchanged files and files with only whitspace changes render with the correct masks.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4398
2013-01-12 00:25:15 +01:00
|
|
|
self::ATTR_WHITELINES => !$hunk_parser->getHasTextChanges(),
|
2014-11-18 00:12:07 +01:00
|
|
|
self::ATTR_MOVEAWAY => $moveaway,
|
2013-01-09 22:11:17 +01:00
|
|
|
));
|
|
|
|
|
|
|
|
$hunk_parser->generateIntraLineDiffs();
|
|
|
|
$hunk_parser->generateVisibileLinesMask();
|
|
|
|
|
|
|
|
$this->setOldLines($hunk_parser->getOldLines());
|
|
|
|
$this->setNewLines($hunk_parser->getNewLines());
|
|
|
|
$this->setIntraLineDiffs($hunk_parser->getIntraLineDiffs());
|
|
|
|
$this->setVisibileLinesMask($hunk_parser->getVisibleLinesMask());
|
2013-01-11 01:06:39 +01:00
|
|
|
$this->hunkStartLines = $hunk_parser->getHunkStartLines(
|
|
|
|
$changeset->getHunks());
|
2013-01-09 22:11:17 +01:00
|
|
|
|
|
|
|
$new_corpus = $hunk_parser->getNewCorpus();
|
|
|
|
$new_corpus_block = implode('', $new_corpus);
|
|
|
|
$this->markGenerated($new_corpus_block);
|
|
|
|
|
|
|
|
if ($this->isTopLevel &&
|
|
|
|
!$this->comments &&
|
|
|
|
($this->isGenerated() ||
|
|
|
|
$this->isUnchanged() ||
|
|
|
|
$this->isDeleted())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$old_corpus = $hunk_parser->getOldCorpus();
|
|
|
|
$old_corpus_block = implode('', $old_corpus);
|
|
|
|
$old_future = $this->getHighlightFuture($old_corpus_block);
|
|
|
|
$new_future = $this->getHighlightFuture($new_corpus_block);
|
|
|
|
$futures = array(
|
|
|
|
'old' => $old_future,
|
|
|
|
'new' => $new_future,
|
|
|
|
);
|
|
|
|
$corpus_blocks = array(
|
|
|
|
'old' => $old_corpus_block,
|
|
|
|
'new' => $new_corpus_block,
|
|
|
|
);
|
2011-05-21 05:40:00 +02:00
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
$this->highlightErrors = false;
|
2014-12-30 13:13:38 +01:00
|
|
|
foreach (new FutureIterator($futures) as $key => $future) {
|
2013-01-09 22:11:17 +01:00
|
|
|
try {
|
|
|
|
try {
|
|
|
|
$highlighted = $future->resolve();
|
|
|
|
} catch (PhutilSyntaxHighlighterException $ex) {
|
|
|
|
$this->highlightErrors = true;
|
|
|
|
$highlighted = id(new PhutilDefaultSyntaxHighlighter())
|
|
|
|
->getHighlightFuture($corpus_blocks[$key])
|
|
|
|
->resolve();
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
2013-01-09 22:11:17 +01:00
|
|
|
switch ($key) {
|
|
|
|
case 'old':
|
|
|
|
$this->oldRender = $this->processHighlightedSource(
|
|
|
|
$this->old,
|
|
|
|
$highlighted);
|
|
|
|
break;
|
|
|
|
case 'new':
|
|
|
|
$this->newRender = $this->processHighlightedSource(
|
|
|
|
$this->new,
|
|
|
|
$highlighted);
|
|
|
|
break;
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
2013-01-09 22:11:17 +01:00
|
|
|
} catch (Exception $ex) {
|
|
|
|
phlog($ex);
|
|
|
|
throw $ex;
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
2013-01-09 22:11:17 +01:00
|
|
|
|
|
|
|
$this->applyIntraline(
|
|
|
|
$this->oldRender,
|
|
|
|
ipull($this->intra, 0),
|
|
|
|
$old_corpus);
|
|
|
|
$this->applyIntraline(
|
|
|
|
$this->newRender,
|
|
|
|
ipull($this->intra, 1),
|
|
|
|
$new_corpus);
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
private function shouldRenderPropertyChangeHeader($changeset) {
|
|
|
|
if (!$this->isTopLevel) {
|
|
|
|
// We render properties only at top level; otherwise we get multiple
|
|
|
|
// copies of them when a user clicks "Show More".
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
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();
|
2014-06-20 20:49:41 +02:00
|
|
|
|
|
|
|
$encoding = null;
|
|
|
|
if ($this->characterEncoding) {
|
|
|
|
// We are forcing this changeset to be interpreted with a specific
|
|
|
|
// character encoding, so force all the hunks into that encoding and
|
|
|
|
// propagate it to the renderer.
|
|
|
|
$encoding = $this->characterEncoding;
|
|
|
|
foreach ($this->changeset->getHunks() as $hunk) {
|
|
|
|
$hunk->forceEncoding($this->characterEncoding);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We're just using the default, so tell the renderer what that is
|
|
|
|
// (by reading the encoding from the first hunk).
|
|
|
|
foreach ($this->changeset->getHunks() as $hunk) {
|
|
|
|
$encoding = $hunk->getDataEncoding();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-26 22:24:43 +02:00
|
|
|
$this->tryCacheStuff();
|
2012-12-08 01:19:57 +01:00
|
|
|
$render_pch = $this->shouldRenderPropertyChangeHeader($this->changeset);
|
|
|
|
|
2012-12-19 19:56:00 +01:00
|
|
|
$rows = max(
|
|
|
|
count($this->old),
|
|
|
|
count($this->new));
|
|
|
|
|
Implement basic one-up and test renderers
Summary:
This is a half-step toward one-up and test renderers. This is mostly a structural change, and has no user-facing impact. It splits the rendering hierarchy like this:
- Renderer (more methods are abstract than before)
- HTML Renderer (most HTML stuff has moved down from the base to here)
- HTML 1-up (placeholder only -- not yet a functional implementation)
- HTML 2-up (minimal changes, uses mostly old code)
- Test Renderer (unit-testable renderer base, implements text versions of the HTML stuff)
- Test 1-up (selects 1-up mode for test rendering)
- Test 2-up (selects 2-up mode for test rendering)
Broadly, I'm trying to share as much code as possible by splitting rendering into more, smaller stages. Specifically, we do this:
- Combine the various sorts of inputs (changes, context, inlines, etc.) into a single, relatively homogenous list of "primitives". This happens in the base class.
- The primitive types are: old (diff left side), new (diff right side), context ("show more context"), no-context ("context not available") and inline (inline comment).
- Possibly, apply a filtering/reordering step to the primitives to get them ready for 1-up rendering. This mostly removes information, and does a small amount of reordering. This also happens in the base class.
- Pass the primitives to the actual renderer, to convert them into HTML, text, or whatever else. This happens in the leaf class.
The primitive implementation is not yet complete (it doesn't attach as much information to the primitives as it should -- stuff like coverage and copies), but covers the basics.
The existing HTMLTwoUp renderer does not use the primitive path; instead, it still goes down the old path.
Test Plan: Ran unit tests, looked at a bunch of diffs.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4421
2013-01-14 23:20:06 +01:00
|
|
|
$renderer = $this->getRenderer()
|
2015-01-30 20:17:34 +01:00
|
|
|
->setUser($this->getUser())
|
2012-12-08 01:19:57 +01:00
|
|
|
->setChangeset($this->changeset)
|
|
|
|
->setRenderPropertyChangeHeader($render_pch)
|
2013-09-13 01:00:00 +02:00
|
|
|
->setIsTopLevel($this->isTopLevel)
|
2012-12-08 01:19:57 +01:00
|
|
|
->setOldRender($this->oldRender)
|
|
|
|
->setNewRender($this->newRender)
|
2013-01-11 01:06:39 +01:00
|
|
|
->setHunkStartLines($this->hunkStartLines)
|
2012-12-08 01:19:57 +01:00
|
|
|
->setOldChangesetID($this->leftSideChangesetID)
|
|
|
|
->setNewChangesetID($this->rightSideChangesetID)
|
|
|
|
->setOldAttachesToNewFile($this->leftSideAttachesToNewFile)
|
|
|
|
->setNewAttachesToNewFile($this->rightSideAttachesToNewFile)
|
2012-12-12 02:16:11 +01:00
|
|
|
->setCodeCoverage($this->getCoverage())
|
2012-12-08 01:19:57 +01:00
|
|
|
->setRenderingReference($this->getRenderingReference())
|
|
|
|
->setMarkupEngine($this->markupEngine)
|
2013-01-15 17:07:40 +01:00
|
|
|
->setHandles($this->handles)
|
|
|
|
->setOldLines($this->old)
|
2014-06-20 20:49:41 +02:00
|
|
|
->setNewLines($this->new)
|
2015-01-30 20:17:34 +01:00
|
|
|
->setOriginalCharacterEncoding($encoding)
|
Track a "Done" state on inline comments
Summary:
Ref T1460. This just barely works, but throwing it up in case any of it sounds mechanically crazy before we build integrations/UI/etc.
Specifically, these are the behaviors:
- You can mark your own draft comments as "done" before you submit them. The intent is to let reviewers mark their stuff advisory/minor/not-important before they submit it, to hint to authors that they don't expect the feedback to necessarily be addressed (maybe it's a joke, maybe it's just discussion, maybe it's "consider..").
- You can mark others' published comments as "done" if you're the revision/commit author. The intent is to keep this lightweight by not requiring an audit trail of who marked what done when. If anyone could mark anything done, we'd have to have some way to show who marked stuff.
- When you mark stuff done (or unmark it), it goes into a "draft" state, where you see the change but others don't see it yet. The intent is twofold:
- Be consistent with how inlines work.
- Allow us to publish a "epriestley updated this revision + epriestley marked 15 inlines as done" story later if we want. This seems more useful than publishing 15 "epriestley marked one thing as done" stories.
- The actual bit where done-ness publishes isn't implemented.
- UI is bare bones.
- No integration with the rest of the UI yet.
Test Plan: Clicked some checkboxes.
Reviewers: chad, btrahan
Reviewed By: btrahan
Subscribers: paulshen, chasemp, epriestley
Maniphest Tasks: T1460
Differential Revision: https://secure.phabricator.com/D12033
2015-03-10 02:41:47 +01:00
|
|
|
->setShowEditAndReplyLinks($this->getShowEditAndReplyLinks())
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02:00
|
|
|
->setCanMarkDone($this->getCanMarkDone())
|
2015-03-27 19:23:10 +01:00
|
|
|
->setObjectOwnerPHID($this->getObjectOwnerPHID())
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02:00
|
|
|
->setHighlightingDisabled($this->highlightingDisabled);
|
2012-12-11 00:12:32 +01:00
|
|
|
|
2012-05-01 07:31:26 +02:00
|
|
|
$shield = null;
|
2012-07-26 22:24:43 +02:00
|
|
|
if ($this->isTopLevel && !$this->comments) {
|
|
|
|
if ($this->isGenerated()) {
|
2012-12-08 01:19:57 +01:00
|
|
|
$shield = $renderer->renderShield(
|
|
|
|
pht(
|
|
|
|
'This file contains generated code, which does not normally '.
|
Fix whitespace and unchanged file shields
Summary:
Fixes T181. I actually have no idea what the original issue in T181 was, but this fixes several problems:
- The code for figuring out whitespace-only changes was extremely confusing and probably buggy (the code for figuring out unchanged files was equally confusing but probably less buggy).
- When rendering a whitespace shield, we did not offer a "Show Changes" link. Instead, show the "Show Changes" link: we can always render the content beneath this link.
- When clicking "Show Changes", we used the current whitespace mode, which might result in a diff with no changes. Instead, force "show all" whitespace mode.
- We never offered a "show changes" link for unchanged files. Instead, offer this link if we can service it.
- When clicking "Show Changes", we pierced the shield but didn't force file content, which would fold the entire file even if it was available. Instead, force the file content.
Test Plan: Generated whitespace-only and no-change diffs. Clicked shield links, or verified no shield link available in no-change-with-no-content. Generated an "@generated" change, verified shield worked correctly.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T181, T2009
Differential Revision: https://secure.phabricator.com/D4407
2013-01-12 00:27:42 +01:00
|
|
|
'need to be reviewed.'));
|
Expand Differential test coverage to include moves, shields, and more
Summary:
See D11468 and D11465. Fixes T5163. Fixes T4105. This makes it practical to test shields, unshielding, moves, etc.
This fixes the issue in D11468, where line maps from whitespace-ignored hunks could have fewer lines than line maps from whitespace-respected hunks, causing a warning.
This encodes the behavior which D11465 changed, making it the canon behavior. Specifically, we do **not** show a shield. I think this is correct. It seems misleading to show "the contents of this file were not changed", because they were changed in both the sense that the file was completely removed, and also changed in the sense that the content itself was (or may have been) changed at the destination. Instead, we just show nothing.
Test Plan:
- Added test coverage.
- Ran tests.
- Used `arc diff --raw --browse` to verify that web behavior was consistent with CLI/test behavior.
Reviewers: joshuaspence, btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T4105, T5163
Differential Revision: https://secure.phabricator.com/D11970
2015-03-05 23:00:26 +01:00
|
|
|
} else if ($this->isMoveAway()) {
|
|
|
|
// We put an empty shield on these files. Normally, they do not have
|
|
|
|
// any diff content anyway. However, if they come through `arc`, they
|
|
|
|
// may have content. We don't want to show it (it's not useful) and
|
|
|
|
// we bailed out of fully processing it earlier anyway.
|
|
|
|
|
|
|
|
// We could show a message like "this file was moved", but we show
|
|
|
|
// that as a change header anyway, so it would be redundant. Instead,
|
|
|
|
// just render an empty shield to skip rendering the diff body.
|
|
|
|
$shield = '';
|
2012-07-26 22:24:43 +02:00
|
|
|
} else if ($this->isUnchanged()) {
|
Fix whitespace and unchanged file shields
Summary:
Fixes T181. I actually have no idea what the original issue in T181 was, but this fixes several problems:
- The code for figuring out whitespace-only changes was extremely confusing and probably buggy (the code for figuring out unchanged files was equally confusing but probably less buggy).
- When rendering a whitespace shield, we did not offer a "Show Changes" link. Instead, show the "Show Changes" link: we can always render the content beneath this link.
- When clicking "Show Changes", we used the current whitespace mode, which might result in a diff with no changes. Instead, force "show all" whitespace mode.
- We never offered a "show changes" link for unchanged files. Instead, offer this link if we can service it.
- When clicking "Show Changes", we pierced the shield but didn't force file content, which would fold the entire file even if it was available. Instead, force the file content.
Test Plan: Generated whitespace-only and no-change diffs. Clicked shield links, or verified no shield link available in no-change-with-no-content. Generated an "@generated" change, verified shield worked correctly.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T181, T2009
Differential Revision: https://secure.phabricator.com/D4407
2013-01-12 00:27:42 +01:00
|
|
|
$type = 'text';
|
|
|
|
if (!$rows) {
|
|
|
|
// NOTE: Normally, diffs which don't change files do not include
|
|
|
|
// file content (for example, if you "chmod +x" a file and then
|
|
|
|
// run "git show", the file content is not available). Similarly,
|
|
|
|
// if you move a file from A to B without changing it, diffs normally
|
|
|
|
// do not show the file content. In some cases `arc` is able to
|
|
|
|
// synthetically generate content for these diffs, but for raw diffs
|
|
|
|
// we'll never have it so we need to be prepared to not render a link.
|
|
|
|
$type = 'none';
|
|
|
|
}
|
2015-03-09 01:45:57 +01:00
|
|
|
|
|
|
|
$type_add = DifferentialChangeType::TYPE_ADD;
|
|
|
|
if ($this->changeset->getChangeType() == $type_add) {
|
|
|
|
// Although the generic message is sort of accurate in a technical
|
|
|
|
// sense, this more-tailored message is less confusing.
|
|
|
|
$shield = $renderer->renderShield(
|
|
|
|
pht('This is an empty file.'),
|
|
|
|
$type);
|
|
|
|
} else {
|
|
|
|
$shield = $renderer->renderShield(
|
|
|
|
pht('The contents of this file were not changed.'),
|
|
|
|
$type);
|
|
|
|
}
|
Simplify hunk parsing
Summary:
Simplify whitespace-ignoring diffing:
- Currently, we generate two diffs (one ignoring whitespace, one normal) and then copy the //text content// from the normal one to the whitespace-ignoring one.
- Instead, copy the //change types// from the ignoring one to the normal one.
- This is cheaper and much simpler.
- It means that we have the right change types in reparseHunksForSpecialAttributes(), and can simplify some other things.
Simplify whitespace changes, unchanged files, and deleted file detections:
- Currently, we do this inline with a bunch of other stuff, in the reparse step.
- Instead, do it separately. This is simpler.
Simplify intraline whitespace handling:
- Currently, this is a complicated mess that makes roughly zero sense.
- Instead, do it in reparse in a straightforward way.
Partially fix handling of files changed only by changing whitespace.
Partially fix handling of unchanged files.
Test Plan:
- Ran unit tests.
- Created context-less diffs, verified they rendered reasonably.
- Generated a diff with prefix whitespace, suffix whitespace, intraline whitespace, and non-whitespace changes. Verified changes rendered correctly in "ignore most" and "show all" modes.
- Verified unchanged files and files with only whitspace changes render with the correct masks.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2009
Differential Revision: https://secure.phabricator.com/D4398
2013-01-12 00:25:15 +01:00
|
|
|
} else if ($this->isWhitespaceOnly()) {
|
|
|
|
$shield = $renderer->renderShield(
|
Fix whitespace and unchanged file shields
Summary:
Fixes T181. I actually have no idea what the original issue in T181 was, but this fixes several problems:
- The code for figuring out whitespace-only changes was extremely confusing and probably buggy (the code for figuring out unchanged files was equally confusing but probably less buggy).
- When rendering a whitespace shield, we did not offer a "Show Changes" link. Instead, show the "Show Changes" link: we can always render the content beneath this link.
- When clicking "Show Changes", we used the current whitespace mode, which might result in a diff with no changes. Instead, force "show all" whitespace mode.
- We never offered a "show changes" link for unchanged files. Instead, offer this link if we can service it.
- When clicking "Show Changes", we pierced the shield but didn't force file content, which would fold the entire file even if it was available. Instead, force the file content.
Test Plan: Generated whitespace-only and no-change diffs. Clicked shield links, or verified no shield link available in no-change-with-no-content. Generated an "@generated" change, verified shield worked correctly.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T181, T2009
Differential Revision: https://secure.phabricator.com/D4407
2013-01-12 00:27:42 +01:00
|
|
|
pht('This file was changed only by adding or removing whitespace.'),
|
|
|
|
'whitespace');
|
2012-07-26 22:24:43 +02:00
|
|
|
} else if ($this->isDeleted()) {
|
2012-12-08 01:19:57 +01:00
|
|
|
$shield = $renderer->renderShield(
|
Fix whitespace and unchanged file shields
Summary:
Fixes T181. I actually have no idea what the original issue in T181 was, but this fixes several problems:
- The code for figuring out whitespace-only changes was extremely confusing and probably buggy (the code for figuring out unchanged files was equally confusing but probably less buggy).
- When rendering a whitespace shield, we did not offer a "Show Changes" link. Instead, show the "Show Changes" link: we can always render the content beneath this link.
- When clicking "Show Changes", we used the current whitespace mode, which might result in a diff with no changes. Instead, force "show all" whitespace mode.
- We never offered a "show changes" link for unchanged files. Instead, offer this link if we can service it.
- When clicking "Show Changes", we pierced the shield but didn't force file content, which would fold the entire file even if it was available. Instead, force the file content.
Test Plan: Generated whitespace-only and no-change diffs. Clicked shield links, or verified no shield link available in no-change-with-no-content. Generated an "@generated" change, verified shield worked correctly.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T181, T2009
Differential Revision: https://secure.phabricator.com/D4407
2013-01-12 00:27:42 +01:00
|
|
|
pht('This file was completely deleted.'));
|
2012-07-26 22:24:43 +02:00
|
|
|
} else if ($this->changeset->getAffectedLineCount() > 2500) {
|
|
|
|
$lines = number_format($this->changeset->getAffectedLineCount());
|
2012-12-08 01:19:57 +01:00
|
|
|
$shield = $renderer->renderShield(
|
|
|
|
pht(
|
2013-01-16 16:26:27 +01:00
|
|
|
'This file has a very large number of changes (%s lines).',
|
Fix whitespace and unchanged file shields
Summary:
Fixes T181. I actually have no idea what the original issue in T181 was, but this fixes several problems:
- The code for figuring out whitespace-only changes was extremely confusing and probably buggy (the code for figuring out unchanged files was equally confusing but probably less buggy).
- When rendering a whitespace shield, we did not offer a "Show Changes" link. Instead, show the "Show Changes" link: we can always render the content beneath this link.
- When clicking "Show Changes", we used the current whitespace mode, which might result in a diff with no changes. Instead, force "show all" whitespace mode.
- We never offered a "show changes" link for unchanged files. Instead, offer this link if we can service it.
- When clicking "Show Changes", we pierced the shield but didn't force file content, which would fold the entire file even if it was available. Instead, force the file content.
Test Plan: Generated whitespace-only and no-change diffs. Clicked shield links, or verified no shield link available in no-change-with-no-content. Generated an "@generated" change, verified shield worked correctly.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T181, T2009
Differential Revision: https://secure.phabricator.com/D4407
2013-01-12 00:27:42 +01:00
|
|
|
$lines));
|
2012-05-01 07:31:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Expand Differential test coverage to include moves, shields, and more
Summary:
See D11468 and D11465. Fixes T5163. Fixes T4105. This makes it practical to test shields, unshielding, moves, etc.
This fixes the issue in D11468, where line maps from whitespace-ignored hunks could have fewer lines than line maps from whitespace-respected hunks, causing a warning.
This encodes the behavior which D11465 changed, making it the canon behavior. Specifically, we do **not** show a shield. I think this is correct. It seems misleading to show "the contents of this file were not changed", because they were changed in both the sense that the file was completely removed, and also changed in the sense that the content itself was (or may have been) changed at the destination. Instead, we just show nothing.
Test Plan:
- Added test coverage.
- Ran tests.
- Used `arc diff --raw --browse` to verify that web behavior was consistent with CLI/test behavior.
Reviewers: joshuaspence, btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T4105, T5163
Differential Revision: https://secure.phabricator.com/D11970
2015-03-05 23:00:26 +01:00
|
|
|
if ($shield !== null) {
|
2012-12-08 01:19:57 +01:00
|
|
|
return $renderer->renderChangesetTable($shield);
|
2012-05-01 07:31:26 +02:00
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
|
Don't highlight very large files by default
Summary:
Ref T5644. See some discussion in D8040.
When a file is very large (more than 64KB of text), don't activate syntax highlighting by default. This should prevent us from wasting resources running `pygmentize` on enormous files.
Users who want the file highlighted can still select "Highlight As...".
The tricky part of this diff is separating the headers into "changeset" headers and "undershield" (rendering) headers. Specifically, a file might have these headers/shields:
- "This file is newly added."
- "This file is generated. Show Changes"
- "Highlighting is disabled for this large file."
In this case, I want the user to see "added" and "generated" when they load the page, and only see "highlighting disabled" after they click "Show Changes". So there are several categories:
- "Changeset" headers, which discuss the changeset as a whole (binary file, image file, moved, added, deleted, etc.)
- "Property" headers, which describe metadata changes (not relevant here).
- "Shields", which hide files from view by default.
- "Undershield" headers, which provide rendering information that is only relevant if there is no shield on the file.
Test Plan:
- Viewed a diff with the library map, clicked "show changes", got a "highlighting disabled" header back with highlighting disabled.
- Enabled highlighting explicitly (this currently restores the shield, which it probably shouldn't, but that feels out of scope for this change). The deshielded file is highlighted per the user's request.
- Loaded context on normal files.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: joshuaspence, epriestley
Maniphest Tasks: T5644
Differential Revision: https://secure.phabricator.com/D12132
2014-06-29 21:07:46 +02:00
|
|
|
// This request should render the "undershield" headers if it's a top-level
|
|
|
|
// request which made it this far (indicating the changeset has no shield)
|
|
|
|
// or it's a request with no mask information (indicating it's the request
|
|
|
|
// that removes the rendering shield). Possibly, this second class of
|
|
|
|
// request might need to be made more explicit.
|
|
|
|
$is_undershield = (empty($mask_force) || $this->isTopLevel);
|
|
|
|
$renderer->setIsUndershield($is_undershield);
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
$old_comments = array();
|
|
|
|
$new_comments = array();
|
|
|
|
$old_mask = array();
|
|
|
|
$new_mask = array();
|
2011-01-25 00:52:35 +01:00
|
|
|
$feedback_mask = array();
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
if ($this->comments) {
|
2015-04-21 15:59:09 +02:00
|
|
|
// If there are any comments which appear in sections of the file which
|
|
|
|
// we don't have, we're going to move them backwards to the closest
|
|
|
|
// earlier line. Two cases where this may happen are:
|
|
|
|
//
|
|
|
|
// - Porting ghost comments forward into a file which was mostly
|
|
|
|
// deleted.
|
|
|
|
// - Porting ghost comments forward from a full-context diff to a
|
|
|
|
// partial-context diff.
|
|
|
|
|
|
|
|
list($old_backmap, $new_backmap) = $this->buildLineBackmaps();
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
foreach ($this->comments as $comment) {
|
2015-04-21 15:59:09 +02:00
|
|
|
$new_side = $this->isCommentOnRightSideWhenDisplayed($comment);
|
|
|
|
|
|
|
|
$line = $comment->getLineNumber();
|
|
|
|
if ($new_side) {
|
|
|
|
$back_line = $new_backmap[$line];
|
|
|
|
} else {
|
|
|
|
$back_line = $old_backmap[$line];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($back_line != $line) {
|
|
|
|
// TODO: This should probably be cleaner, but just be simple and
|
|
|
|
// obvious for now.
|
|
|
|
$ghost = $comment->getIsGhost();
|
|
|
|
if ($ghost) {
|
|
|
|
$moved = pht(
|
|
|
|
'This comment originally appeared on line %s, but that line '.
|
|
|
|
'does not exist in this version of the diff. It has been '.
|
|
|
|
'moved backward to the nearest line.',
|
|
|
|
new PhutilNumber($line));
|
|
|
|
$ghost['reason'] = $ghost['reason']."\n\n".$moved;
|
|
|
|
$comment->setIsGhost($ghost);
|
|
|
|
}
|
|
|
|
|
|
|
|
$comment->setLineNumber($back_line);
|
|
|
|
$comment->setLineLength(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
$start = max($comment->getLineNumber() - self::LINES_CONTEXT, 0);
|
|
|
|
$end = $comment->getLineNumber() +
|
|
|
|
$comment->getLineLength() +
|
|
|
|
self::LINES_CONTEXT;
|
|
|
|
for ($ii = $start; $ii <= $end; $ii++) {
|
|
|
|
if ($new_side) {
|
|
|
|
$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;
|
|
|
|
}
|
|
|
|
}
|
2015-04-21 15:59:09 +02:00
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
$this->comments = msort($this->comments, 'getID');
|
|
|
|
foreach ($this->comments as $comment) {
|
|
|
|
$final = $comment->getLineNumber() +
|
|
|
|
$comment->getLineLength();
|
|
|
|
$final = max(1, $final);
|
|
|
|
if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
|
|
|
|
$new_comments[$final][] = $comment;
|
|
|
|
} else {
|
|
|
|
$old_comments[$final][] = $comment;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$renderer
|
|
|
|
->setOldComments($old_comments)
|
|
|
|
->setNewComments($new_comments);
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
switch ($this->changeset->getFileType()) {
|
|
|
|
case DifferentialChangeType::FILE_IMAGE:
|
|
|
|
$old = null;
|
2012-12-13 18:48:28 +01:00
|
|
|
$new = null;
|
2011-09-19 08:04:03 +02:00
|
|
|
// TODO: Improve the architectural issue as discussed in D955
|
|
|
|
// https://secure.phabricator.com/D955
|
2012-12-08 01:19:57 +01:00
|
|
|
$reference = $this->getRenderingReference();
|
2011-09-19 08:04:03 +02:00
|
|
|
$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);
|
2013-08-30 17:15:10 +02:00
|
|
|
$old_phid = null;
|
|
|
|
$new_phid = null;
|
|
|
|
|
|
|
|
// TODO: This is spooky, see D6851
|
|
|
|
if ($vs_changeset) {
|
|
|
|
$vs_metadata = $vs_changeset->getMetadata();
|
|
|
|
$old_phid = idx($vs_metadata, 'new:binary-phid');
|
|
|
|
}
|
2011-09-19 08:04:03 +02:00
|
|
|
|
|
|
|
$changeset = id(new DifferentialChangeset())->load($id);
|
2013-08-30 17:15:10 +02:00
|
|
|
if ($changeset) {
|
|
|
|
$metadata = $changeset->getMetadata();
|
|
|
|
$new_phid = idx($metadata, 'new:binary-phid');
|
|
|
|
}
|
2011-09-19 08:04:03 +02:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
2015-01-30 20:17:34 +01:00
|
|
|
$files = id(new PhabricatorFileQuery())
|
|
|
|
->setViewer($this->getUser())
|
|
|
|
->withPHIDs($file_phids)
|
|
|
|
->execute();
|
2012-01-10 23:48:55 +01:00
|
|
|
foreach ($files as $file) {
|
|
|
|
if (empty($file)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ($file->getPHID() == $old_phid) {
|
2012-12-08 01:19:57 +01:00
|
|
|
$old = $file;
|
|
|
|
} else if ($file->getPHID() == $new_phid) {
|
|
|
|
$new = $file;
|
2012-01-10 23:48:55 +01:00
|
|
|
}
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
2012-12-13 18:48:28 +01:00
|
|
|
|
2014-08-13 23:41:06 +02:00
|
|
|
$renderer->attachOldFile($old);
|
|
|
|
$renderer->attachNewFile($new);
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
return $renderer->renderFileChange($old, $new, $id, $vs);
|
2011-01-25 00:52:35 +01:00
|
|
|
case DifferentialChangeType::FILE_DIRECTORY:
|
|
|
|
case DifferentialChangeType::FILE_BINARY:
|
2012-12-08 01:19:57 +01:00
|
|
|
$output = $renderer->renderChangesetTable(null);
|
2011-01-25 00:52:35 +01:00
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
2012-12-10 10:47:58 +01:00
|
|
|
if ($this->originalLeft && $this->originalRight) {
|
2012-12-08 01:19:57 +01:00
|
|
|
list($highlight_old, $highlight_new) = $this->diffOriginals();
|
|
|
|
$highlight_old = array_flip($highlight_old);
|
|
|
|
$highlight_new = array_flip($highlight_new);
|
|
|
|
$renderer
|
|
|
|
->setHighlightOld($highlight_old)
|
|
|
|
->setHighlightNew($highlight_new);
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
2012-12-08 01:19:57 +01:00
|
|
|
$renderer
|
|
|
|
->setOriginalOld($this->originalLeft)
|
|
|
|
->setOriginalNew($this->originalRight);
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2012-12-12 02:16:11 +01:00
|
|
|
if ($range_start === null) {
|
|
|
|
$range_start = 0;
|
|
|
|
}
|
|
|
|
if ($range_len === null) {
|
|
|
|
$range_len = $rows;
|
|
|
|
}
|
|
|
|
$range_len = min($range_len, $rows - $range_start);
|
|
|
|
|
|
|
|
list($gaps, $mask, $depths) = $this->calculateGapsMaskAndDepths(
|
|
|
|
$mask_force,
|
|
|
|
$feedback_mask,
|
|
|
|
$range_start,
|
2013-02-19 22:33:10 +01:00
|
|
|
$range_len);
|
2012-12-12 02:16:11 +01:00
|
|
|
|
|
|
|
$renderer
|
|
|
|
->setGaps($gaps)
|
|
|
|
->setMask($mask)
|
|
|
|
->setDepths($depths);
|
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
$html = $renderer->renderTextChange(
|
2011-01-25 00:52:35 +01:00
|
|
|
$range_start,
|
|
|
|
$range_len,
|
2013-02-19 22:33:10 +01:00
|
|
|
$rows);
|
2011-01-25 00:52:35 +01:00
|
|
|
|
2012-12-08 01:19:57 +01:00
|
|
|
return $renderer->renderChangesetTable($html);
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2012-12-12 02:16:11 +01:00
|
|
|
/**
|
|
|
|
* This function calculates a lot of stuff we need to know to display
|
|
|
|
* the diff:
|
|
|
|
*
|
|
|
|
* Gaps - compute gaps in the visible display diff, where we will render
|
|
|
|
* "Show more context" spacers. 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. A given $gap
|
|
|
|
* is a tuple of $gap_line_number_start and $gap_length.
|
|
|
|
*
|
|
|
|
* Mask - compute the actual lines that need to be shown (because they
|
|
|
|
* are near changes lines, near inline comments, or the request has
|
|
|
|
* explicitly asked for them, i.e. resulting from the user clicking
|
|
|
|
* "show more"). The $mask returned is a sparesely populated dictionary
|
|
|
|
* of $visible_line_number => true.
|
|
|
|
*
|
|
|
|
* Depths - compute how indented any given line is. The $depths returned
|
|
|
|
* is a sparesely populated dictionary of $visible_line_number => $depth.
|
|
|
|
*
|
|
|
|
* This function also has the side effect of modifying member variable
|
|
|
|
* new such that tabs are normalized to spaces for each line of the diff.
|
|
|
|
*
|
|
|
|
* @return array($gaps, $mask, $depths)
|
|
|
|
*/
|
2015-05-22 09:27:56 +02:00
|
|
|
private function calculateGapsMaskAndDepths(
|
|
|
|
$mask_force,
|
|
|
|
$feedback_mask,
|
|
|
|
$range_start,
|
|
|
|
$range_len) {
|
2012-12-12 02:16:11 +01:00
|
|
|
|
|
|
|
// Calculate gaps and mask first
|
|
|
|
$gaps = array();
|
|
|
|
$gap_start = 0;
|
|
|
|
$in_gap = false;
|
|
|
|
$base_mask = $this->visible + $mask_force + $feedback_mask;
|
|
|
|
$base_mask[$range_start + $range_len] = true;
|
|
|
|
for ($ii = $range_start; $ii <= $range_start + $range_len; $ii++) {
|
|
|
|
if (isset($base_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++) {
|
|
|
|
$base_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);
|
|
|
|
$mask = $base_mask;
|
|
|
|
|
|
|
|
// Time to calculate depth.
|
|
|
|
// We need to go backwards to properly indent whitespace in this code:
|
|
|
|
//
|
|
|
|
// 0: class C {
|
|
|
|
// 1:
|
|
|
|
// 1: function f() {
|
|
|
|
// 2:
|
|
|
|
// 2: return;
|
2013-06-28 18:40:00 +02:00
|
|
|
// 1:
|
|
|
|
// 1: }
|
|
|
|
// 0:
|
|
|
|
// 0: }
|
2012-12-12 02:16:11 +01:00
|
|
|
//
|
|
|
|
$depths = array();
|
|
|
|
$last_depth = 0;
|
|
|
|
$range_end = $range_start + $range_len;
|
|
|
|
if (!isset($this->new[$range_end])) {
|
|
|
|
$range_end--;
|
|
|
|
}
|
|
|
|
for ($ii = $range_end; $ii >= $range_start; $ii--) {
|
|
|
|
// We need to expand tabs to process mixed indenting and to round
|
|
|
|
// correctly later.
|
2014-06-09 20:36:49 +02:00
|
|
|
$line = str_replace("\t", ' ', $this->new[$ii]['text']);
|
2012-12-12 02:16:11 +01:00
|
|
|
$trimmed = ltrim($line);
|
|
|
|
if ($trimmed != '') {
|
|
|
|
// We round down to flatten "/**" and " *".
|
|
|
|
$last_depth = floor((strlen($line) - strlen($trimmed)) / 2);
|
|
|
|
}
|
|
|
|
$depths[$ii] = $last_depth;
|
|
|
|
}
|
|
|
|
|
|
|
|
return array($gaps, $mask, $depths);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2014-06-20 20:49:41 +02:00
|
|
|
$changeset_id = $comment->getChangesetID();
|
|
|
|
$is_new = $comment->getIsNewFile();
|
2011-05-09 06:22:25 +02:00
|
|
|
|
2014-06-20 20:49:41 +02:00
|
|
|
if ($changeset_id == $this->rightSideChangesetID &&
|
2011-05-09 06:22:25 +02:00
|
|
|
$is_new == $this->rightSideAttachesToNewFile) {
|
2014-06-20 20:49:41 +02:00
|
|
|
return true;
|
|
|
|
}
|
2011-05-09 06:22:25 +02:00
|
|
|
|
2014-06-20 20:49:41 +02:00
|
|
|
if ($changeset_id == $this->leftSideChangesetID &&
|
2011-05-09 06:22:25 +02:00
|
|
|
$is_new == $this->leftSideAttachesToNewFile) {
|
2014-06-20 20:49:41 +02:00
|
|
|
return true;
|
|
|
|
}
|
2011-05-09 06:22:25 +02:00
|
|
|
|
|
|
|
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)) {
|
2015-05-22 09:27:56 +02:00
|
|
|
throw new Exception(pht('Comment is not visible on changeset!'));
|
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;
|
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
|
|
|
}
|
|
|
|
|
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() {
|
2013-02-13 03:46:01 +01:00
|
|
|
$na = phutil_tag('em', array(), '-');
|
2012-03-13 01:06:55 +01:00
|
|
|
|
2012-12-12 02:16:11 +01:00
|
|
|
$coverage = $this->getCoverage();
|
|
|
|
if (!$coverage) {
|
2012-03-13 01:06:55 +01:00
|
|
|
return $na;
|
|
|
|
}
|
|
|
|
|
|
|
|
$covered = 0;
|
|
|
|
$not_covered = 0;
|
|
|
|
|
|
|
|
foreach ($this->new as $k => $new) {
|
|
|
|
if (!$new['line']) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$new['type']) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-12-12 02:16:11 +01:00
|
|
|
if (empty($coverage[$new['line'] - 1])) {
|
2012-03-13 01:06:55 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-12-12 02:16:11 +01:00
|
|
|
switch ($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)));
|
|
|
|
}
|
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
public function detectCopiedCode(
|
|
|
|
array $changesets,
|
|
|
|
$min_width = 30,
|
|
|
|
$min_lines = 3) {
|
|
|
|
|
|
|
|
assert_instances_of($changesets, 'DifferentialChangeset');
|
|
|
|
|
|
|
|
$map = array();
|
|
|
|
$files = array();
|
|
|
|
$types = array();
|
|
|
|
foreach ($changesets as $changeset) {
|
|
|
|
$file = $changeset->getFilename();
|
|
|
|
foreach ($changeset->getHunks() as $hunk) {
|
2015-03-24 21:12:09 +01:00
|
|
|
$lines = $hunk->getStructuredOldFile();
|
|
|
|
foreach ($lines as $line => $info) {
|
|
|
|
$type = $info['type'];
|
|
|
|
if ($type == '\\') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$types[$file][$line] = $type;
|
|
|
|
|
|
|
|
$text = $info['text'];
|
|
|
|
$text = trim($text);
|
|
|
|
$files[$file][$line] = $text;
|
|
|
|
|
|
|
|
if (strlen($text) >= $min_width) {
|
|
|
|
$map[$text][] = array($file, $line);
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($changesets as $changeset) {
|
|
|
|
$copies = array();
|
|
|
|
foreach ($changeset->getHunks() as $hunk) {
|
2015-03-24 21:12:09 +01:00
|
|
|
$added = $hunk->getStructuredNewFile();
|
2015-03-24 21:12:24 +01:00
|
|
|
$atype = array();
|
2015-03-24 21:12:09 +01:00
|
|
|
|
|
|
|
foreach ($added as $line => $info) {
|
2015-03-24 21:12:24 +01:00
|
|
|
$atype[$line] = $info['type'];
|
2015-03-24 21:12:09 +01:00
|
|
|
$added[$line] = trim($info['text']);
|
|
|
|
}
|
2014-05-19 21:39:12 +02:00
|
|
|
|
2015-03-24 21:12:09 +01:00
|
|
|
$skip_lines = 0;
|
|
|
|
foreach ($added as $line => $code) {
|
|
|
|
if ($skip_lines) {
|
|
|
|
// We're skipping lines that we already processed because we
|
|
|
|
// extended a block above them downward to include them.
|
|
|
|
$skip_lines--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-03-24 21:12:24 +01:00
|
|
|
if ($atype[$line] !== '+') {
|
|
|
|
// This line hasn't been changed in the new file, so don't try
|
|
|
|
// to figure out where it came from.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-03-24 21:12:09 +01:00
|
|
|
if (empty($map[$code])) {
|
|
|
|
// This line was too short to trigger copy/move detection.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($map[$code]) > 16) {
|
|
|
|
// If there are a large number of identical lines in this diff,
|
|
|
|
// don't try to figure out where this block came from: the analysis
|
|
|
|
// is O(N^2), since we need to compare every line against every
|
|
|
|
// other line. Even if we arrive at a result, it is unlikely to be
|
|
|
|
// meaningful. See T5041.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$best_length = 0;
|
|
|
|
|
|
|
|
// Explore all candidates.
|
|
|
|
foreach ($map[$code] as $val) {
|
|
|
|
list($file, $orig_line) = $val;
|
|
|
|
$length = 1;
|
|
|
|
|
|
|
|
// Search backward and forward to find all of the adjacent lines
|
|
|
|
// which match.
|
|
|
|
foreach (array(-1, 1) as $direction) {
|
|
|
|
$offset = $direction;
|
|
|
|
while (true) {
|
|
|
|
if (isset($copies[$line + $offset])) {
|
|
|
|
// If we run into a block above us which we've already
|
|
|
|
// attributed to a move or copy from elsewhere, stop
|
|
|
|
// looking.
|
|
|
|
break;
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
2015-03-24 21:12:09 +01:00
|
|
|
|
|
|
|
if (!isset($added[$line + $offset])) {
|
|
|
|
// If we've run off the beginning or end of the new file,
|
|
|
|
// stop looking.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isset($files[$file][$orig_line + $offset])) {
|
|
|
|
// If we've run off the beginning or end of the original
|
|
|
|
// file, we also stop looking.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
$old = $files[$file][$orig_line + $offset];
|
|
|
|
$new = $added[$line + $offset];
|
|
|
|
if ($old !== $new) {
|
|
|
|
// If the old line doesn't match the new line, stop
|
|
|
|
// looking.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
$length++;
|
|
|
|
$offset += $direction;
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
|
|
|
}
|
2015-03-24 21:12:09 +01:00
|
|
|
|
|
|
|
if ($length < $best_length) {
|
|
|
|
// If we already know of a better source (more matching lines)
|
|
|
|
// for this move/copy, stick with that one. We prefer long
|
|
|
|
// copies/moves which match a lot of context over short ones.
|
|
|
|
continue;
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
2015-03-24 21:12:09 +01:00
|
|
|
|
|
|
|
if ($length == $best_length) {
|
|
|
|
if (idx($types[$file], $orig_line) != '-') {
|
|
|
|
// If we already know of an equally good source (same number
|
|
|
|
// of matching lines) and this isn't a move, stick with the
|
|
|
|
// other one. We prefer moves over copies.
|
|
|
|
continue;
|
|
|
|
}
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
2015-03-24 21:12:09 +01:00
|
|
|
|
|
|
|
$best_length = $length;
|
|
|
|
// ($offset - 1) contains number of forward matching lines.
|
|
|
|
$best_offset = $offset - 1;
|
|
|
|
$best_file = $file;
|
|
|
|
$best_line = $orig_line;
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
2015-03-24 21:12:09 +01:00
|
|
|
|
|
|
|
$file = ($best_file == $changeset->getFilename() ? '' : $best_file);
|
|
|
|
for ($i = $best_length; $i--; ) {
|
|
|
|
$type = idx($types[$best_file], $best_line + $best_offset - $i);
|
|
|
|
$copies[$line + $best_offset - $i] = ($best_length < $min_lines
|
|
|
|
? array() // Ignore short blocks.
|
|
|
|
: array($file, $best_line + $best_offset - $i, $type));
|
|
|
|
}
|
|
|
|
|
|
|
|
$skip_lines = $best_offset;
|
2013-01-09 22:11:17 +01:00
|
|
|
}
|
|
|
|
}
|
2015-03-24 21:12:09 +01:00
|
|
|
|
2013-01-09 22:11:17 +01:00
|
|
|
$copies = array_filter($copies);
|
|
|
|
if ($copies) {
|
|
|
|
$metadata = $changeset->getMetadata();
|
|
|
|
$metadata['copy:lines'] = $copies;
|
|
|
|
$changeset->setMetadata($metadata);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $changesets;
|
|
|
|
}
|
|
|
|
|
2015-04-21 15:59:09 +02:00
|
|
|
/**
|
|
|
|
* Build maps from lines comments appear on to actual lines.
|
|
|
|
*/
|
|
|
|
private function buildLineBackmaps() {
|
|
|
|
$old_back = array();
|
|
|
|
$new_back = array();
|
|
|
|
foreach ($this->old as $ii => $old) {
|
|
|
|
$old_back[$old['line']] = $old['line'];
|
|
|
|
}
|
|
|
|
foreach ($this->new as $ii => $new) {
|
|
|
|
$new_back[$new['line']] = $new['line'];
|
|
|
|
}
|
|
|
|
|
|
|
|
$max_old_line = 0;
|
|
|
|
$max_new_line = 0;
|
|
|
|
foreach ($this->comments as $comment) {
|
|
|
|
if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
|
|
|
|
$max_new_line = max($max_new_line, $comment->getLineNumber());
|
|
|
|
} else {
|
|
|
|
$max_old_line = max($max_old_line, $comment->getLineNumber());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$cursor = 1;
|
|
|
|
for ($ii = 1; $ii <= $max_old_line; $ii++) {
|
|
|
|
if (empty($old_back[$ii])) {
|
|
|
|
$old_back[$ii] = $cursor;
|
|
|
|
} else {
|
|
|
|
$cursor = $old_back[$ii];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$cursor = 1;
|
|
|
|
for ($ii = 1; $ii <= $max_new_line; $ii++) {
|
|
|
|
if (empty($new_back[$ii])) {
|
|
|
|
$new_back[$ii] = $cursor;
|
|
|
|
} else {
|
|
|
|
$cursor = $new_back[$ii];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array($old_back, $new_back);
|
|
|
|
}
|
|
|
|
|
2011-01-25 00:52:35 +01:00
|
|
|
}
|