@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make DifferentialChangesetParser explicitly map display to storage for comments

Summary:
This is a followup to D228. Basically, we use "changeset" (and, implicitly,
changesetID) for way too much stuff now.

One thing it can't possibly capture is the complete, arbitrary mapping between
the left and right sides of the displayed diff and the places we want to store
the left and right side comments. This causes a bunch of bugs; basically adding
inline comments is completely broken in diff-of-diff views prior to this patch.
Make this mapping explicit.

Note that the renderer already passes this mapping to
DifferentialChangesetParser which is why there are no changes outside this file,
I just didn't finish the implementation during the port.

This has the nice side-effect of fixing T132 and several other bugs.

Test Plan:
Made new-file and old-file comments on a normal diff; reloaded page, verified
comments didn't do anything crazy.

Expanded text on a normal diff, made new-file and old-file comments; reloaded
page, verified comments.

Repeated these steps for a previous diff in the same revision; verified
comments.

Loaded diff-of-diffs and verified expected comments appeared. Made new left and
right hand side comments, which almost work, see below.

NOTE: There is still a bug where comments made in the left-display-side of a
diff-of-diffs will incorrectly be written to the right-storage-side of the
right-display-side diff. However, this is an issue with the JS (the PHP is
correct) so I want to pull it out of scope for this patch since I think I need
to fix some other JS stuff too and this improves the overall state of the world
even if not everything is fixed.

Reviewed By: tuomaspelkonen
Reviewers: jungejason, tuomaspelkonen, aran, ola
CC: aran, tuomaspelkonen, epriestley
Differential Revision: 237

+137 -26
+13 -4
src/applications/differential/controller/changesetview/DifferentialChangesetViewController.php
··· 154 154 $parser->setLeftSideCommentMapping($left_source, $left_new); 155 155 $parser->setWhitespaceMode($request->getStr('whitespace')); 156 156 157 + // Load both left-side and right-side inline comments. 158 + $inlines = $this->loadInlineComments( 159 + array($left_source, $right_source), 160 + $author_phid); 161 + 157 162 $phids = array(); 158 - $inlines = $this->loadInlineComments($id, $author_phid); 159 163 foreach ($inlines as $inline) { 160 164 $parser->parseInlineComment($inline); 161 165 $phids[$inline->getAuthorPHID()] = true; ··· 228 232 )); 229 233 } 230 234 231 - private function loadInlineComments($changeset_id, $author_phid) { 235 + private function loadInlineComments(array $changeset_ids, $author_phid) { 236 + $changeset_ids = array_unique(array_filter($changeset_ids)); 237 + if (!$changeset_ids) { 238 + return; 239 + } 240 + 232 241 return id(new DifferentialInlineComment())->loadAllWhere( 233 - 'changesetID = %d AND (commentID IS NOT NULL OR authorPHID = %s)', 234 - $changeset_id, 242 + 'changesetID IN (%Ld) AND (commentID IS NOT NULL OR authorPHID = %s)', 243 + $changeset_ids, 235 244 $author_phid); 236 245 } 237 246
+124 -22
src/applications/differential/parser/changeset/DifferentialChangesetParser.php
··· 39 39 protected $whitespaceMode = null; 40 40 41 41 protected $subparser; 42 - protected $oldChangesetID = null; 43 42 protected $noHighlight; 44 43 45 44 protected $renderCacheKey = null; ··· 47 46 private $handles; 48 47 private $user; 49 48 49 + private $leftSideChangesetID; 50 + private $leftSideAttachesToNewFile; 51 + 52 + private $rightSideChangesetID; 53 + private $rightSideAttachesToNewFile; 54 + 50 55 const CACHE_VERSION = 4; 51 56 52 57 const ATTR_GENERATED = 'attr:generated'; ··· 60 65 const WHITESPACE_IGNORE_TRAILING = 'ignore-trailing'; 61 66 const WHITESPACE_IGNORE_ALL = 'ignore-all'; 62 67 68 + /** 69 + * Configure which Changeset comments added to the right side of the visible 70 + * diff will be attached to. The ID must be the ID of a real Differential 71 + * Changeset. 72 + * 73 + * The complexity here is that we may show an arbitrary side of an arbitrary 74 + * changeset as either the left or right part of a diff. This method allows 75 + * the left and right halves of the displayed diff to be correctly mapped to 76 + * storage changesets. 77 + * 78 + * @param id The Differential Changeset ID that comments added to the right 79 + * side of the visible diff should be attached to. 80 + * @param bool If true, attach new comments to the right side of the storage 81 + * changeset. Note that this may be false, if the left side of 82 + * some storage changeset is being shown as the right side of 83 + * a display diff. 84 + * @return this 85 + */ 63 86 public function setRightSideCommentMapping($id, $is_new) { 64 - 87 + $this->rightSideChangesetID = $id; 88 + $this->rightSideAttachesToNewFile = $is_new; 89 + return $this; 65 90 } 66 91 92 + /** 93 + * See setRightSideCommentMapping(), but this sets information for the left 94 + * side of the display diff. 95 + */ 67 96 public function setLeftSideCommentMapping($id, $is_new) { 68 - 97 + $this->leftSideChangesetID = $id; 98 + $this->leftSideAttachesToNewFile = $is_new; 99 + return $this; 69 100 } 70 101 71 102 /** ··· 101 132 102 133 public function setWhitespaceMode($whitespace_mode) { 103 134 $this->whitespaceMode = $whitespace_mode; 104 - return $this; 105 - } 106 - 107 - public function setOldChangesetID($old_changeset_id) { 108 - $this->oldChangesetID = $old_changeset_id; 109 135 return $this; 110 136 } 111 137 ··· 236 262 } 237 263 238 264 public function parseInlineComment(DifferentialInlineComment $comment) { 239 - $this->comments[] = $comment; 265 + // Parse only comments which are actually visible. 266 + if ($this->isCommentVisibleOnRenderedDiff($comment)) { 267 + $this->comments[] = $comment; 268 + } 240 269 return $this; 241 270 } 242 271 ··· 837 866 $end = $comment->getLineNumber() + 838 867 $comment->getLineLength() + 839 868 self::LINES_CONTEXT; 840 - $new = $this->isCommentInNewFile($comment); 869 + $new = $this->isCommentOnRightSideWhenDisplayed($comment); 841 870 for ($ii = $start; $ii <= $end; $ii++) { 842 871 if ($new) { 843 872 $new_mask[$ii] = true; ··· 862 891 foreach ($this->comments as $comment) { 863 892 $final = $comment->getLineNumber() + 864 893 $comment->getLineLength(); 865 - if ($this->isCommentInNewFile($comment)) { 894 + if ($this->isCommentOnRightSideWhenDisplayed($comment)) { 866 895 $new_comments[$final][] = $comment; 867 896 } else { 868 897 $old_comments[$final][] = $comment; ··· 881 910 return $this->renderChangesetTable($this->changeset, $html); 882 911 } 883 912 884 - private function isCommentInNewFile(DifferentialInlineComment $comment) { 885 - if ($this->oldChangesetID) { 886 - return ($comment->getChangesetID() != $this->oldChangesetID); 887 - } else { 888 - return $comment->getIsNewFile(); 913 + /** 914 + * Determine if an inline comment will appear on the rendered diff, 915 + * taking into consideration which halves of which changesets will actually 916 + * be shown. 917 + * 918 + * @param DifferentialInlineComment Comment to test for visibility. 919 + * @return bool True if the comment is visible on the rendered diff. 920 + */ 921 + private function isCommentVisibleOnRenderedDiff( 922 + DifferentialInlineComment $comment) { 923 + 924 + $changeset_id = $comment->getChangesetID(); 925 + $is_new = $comment->getIsNewFile(); 926 + 927 + if ($changeset_id == $this->rightSideChangesetID && 928 + $is_new == $this->rightSideAttachesToNewFile) { 929 + return true; 889 930 } 931 + 932 + if ($changeset_id == $this->leftSideChangesetID && 933 + $is_new == $this->leftSideAttachesToNewFile) { 934 + return true; 935 + } 936 + 937 + return false; 938 + } 939 + 940 + 941 + /** 942 + * Determine if a comment will appear on the right side of the display diff. 943 + * Note that the comment must appear somewhere on the rendered changeset, as 944 + * per isCommentVisibleOnRenderedDiff(). 945 + * 946 + * @param DifferentialInlineComment Comment to test for display location. 947 + * @return bool True for right, false for left. 948 + */ 949 + private function isCommentOnRightSideWhenDisplayed( 950 + DifferentialInlineComment $comment) { 951 + 952 + if (!$this->isCommentVisibleOnRenderedDiff($comment)) { 953 + throw new Exception("Comment is not visible on changeset!"); 954 + } 955 + 956 + $changeset_id = $comment->getChangesetID(); 957 + $is_new = $comment->getIsNewFile(); 958 + 959 + if ($changeset_id == $this->rightSideChangesetID && 960 + $is_new == $this->rightSideAttachesToNewFile) { 961 + return true; 962 + } 963 + 964 + return false; 890 965 } 891 966 892 967 protected function renderShield($message, $more) { ··· 961 1036 962 1037 $range_len = min($range_len, $rows - $range_start); 963 1038 1039 + // Gaps - compute gaps in the visible display diff, where we will render 1040 + // "Show more context" spacers. This builds an aggregate $mask of all the 1041 + // lines we must show (because they are near changed lines, near inline 1042 + // comments, or the request has explicitly asked for them, i.e. resulting 1043 + // from the user clicking "show more") and then finds all the gaps between 1044 + // visible lines. If a gap is smaller than the context size, we just 1045 + // display it. Otherwise, we record it into $gaps and will render a 1046 + // "show more context" element instead of diff text below. 1047 + 964 1048 $gaps = array(); 965 1049 $gap_start = 0; 966 1050 $in_gap = false; ··· 989 1073 990 1074 $gaps = array_reverse($gaps); 991 1075 992 - $changeset = $this->changesetID; 993 1076 $reference = $this->getChangeset()->getRenderingReference(); 994 1077 1078 + $left_id = $this->leftSideChangesetID; 1079 + $right_id = $this->rightSideChangesetID; 1080 + 1081 + // "N" stands for 'new' and means the comment should attach to the new file 1082 + // when stored, i.e. DifferentialInlineComment->setIsNewFile(). 1083 + // "O" stands for 'old' and means the comment should attach to the old file. 1084 + 1085 + $left_char = $this->leftSideAttachesToNewFile 1086 + ? 'N' 1087 + : 'O'; 1088 + $right_char = $this->rightSideAttachesToNewFile 1089 + ? 'N' 1090 + : 'O'; 1091 + 995 1092 for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) { 996 1093 if (empty($mask[$ii])) { 1094 + // If we aren't going to show this line, we've just entered a gap. 1095 + // Pop information about the next gap off the $gaps stack and render 1096 + // an appropriate "Show more context" element. This branch eventually 1097 + // increments $ii by the entire size of the gap and then continues 1098 + // the loop. 997 1099 $gap = array_pop($gaps); 998 1100 $top = $gap[0]; 999 1101 $len = $gap[1]; ··· 1100 1202 $html[] = $context_not_available; 1101 1203 } 1102 1204 1103 - if ($o_num && $changeset) { 1104 - $o_id = ' id="C'.$changeset.'OL'.$o_num.'"'; 1205 + if ($o_num && $left_id) { 1206 + $o_id = ' id="C'.$left_id.$left_char.'L'.$o_num.'"'; 1105 1207 } else { 1106 1208 $o_id = null; 1107 1209 } 1108 1210 1109 - if ($n_num && $changeset) { 1110 - $n_id = ' id="C'.$changeset.'NL'.$n_num.'"'; 1211 + if ($n_num && $right_id) { 1212 + $n_id = ' id="C'.$right_id.$right_char.'L'.$n_num.'"'; 1111 1213 } else { 1112 1214 $n_id = null; 1113 1215 } ··· 1156 1258 ($comment->getAuthorPHID() == $user->getPHID()) && 1157 1259 (!$comment->getCommentID()); 1158 1260 1159 - $on_right = $this->isCommentInNewFile($comment); 1261 + $on_right = $this->isCommentOnRightSideWhenDisplayed($comment); 1160 1262 1161 1263 return id(new DifferentialInlineCommentView()) 1162 1264 ->setInlineComment($comment)