Page MenuHomePhabricator

D21276.id50661.diff
No OneTemporary

D21276.id50661.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -12,8 +12,8 @@
'core.pkg.css' => 'ba768cdb',
'core.pkg.js' => '845355f4',
'dark-console.pkg.js' => '187792c2',
- 'differential.pkg.css' => '42a2334f',
- 'differential.pkg.js' => 'd0ddfb19',
+ 'differential.pkg.css' => 'f924dbcf',
+ 'differential.pkg.js' => '256a327a',
'diffusion.pkg.css' => '42c75c37',
'diffusion.pkg.js' => 'a98c0bf7',
'maniphest.pkg.css' => '35995d6d',
@@ -65,7 +65,7 @@
'rsrc/css/application/differential/add-comment.css' => '7e5900d9',
'rsrc/css/application/differential/changeset-view.css' => '60c3d405',
'rsrc/css/application/differential/core.css' => '7300a73e',
- 'rsrc/css/application/differential/phui-inline-comment.css' => 'd5749acc',
+ 'rsrc/css/application/differential/phui-inline-comment.css' => '4107254a',
'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d',
'rsrc/css/application/differential/revision-history.css' => '8aa3eac5',
'rsrc/css/application/differential/revision-list.css' => '93d2df7d',
@@ -381,7 +381,7 @@
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8',
'rsrc/js/application/diff/DiffChangeset.js' => '6e5e03d2',
'rsrc/js/application/diff/DiffChangesetList.js' => 'b51ba93a',
- 'rsrc/js/application/diff/DiffInline.js' => '6fa445ef',
+ 'rsrc/js/application/diff/DiffInline.js' => '829b88bf',
'rsrc/js/application/diff/DiffPathView.js' => '8207abf9',
'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b',
'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd',
@@ -776,7 +776,7 @@
'phabricator-dashboard-css' => '5a205b9d',
'phabricator-diff-changeset' => '6e5e03d2',
'phabricator-diff-changeset-list' => 'b51ba93a',
- 'phabricator-diff-inline' => '6fa445ef',
+ 'phabricator-diff-inline' => '829b88bf',
'phabricator-diff-path-view' => '8207abf9',
'phabricator-diff-tree-view' => '5d83623b',
'phabricator-drag-and-drop-file-upload' => '4370900d',
@@ -854,7 +854,7 @@
'phui-icon-view-css' => '4cbc684a',
'phui-image-mask-css' => '62c7f4d2',
'phui-info-view-css' => 'a10a909b',
- 'phui-inline-comment-view-css' => 'd5749acc',
+ 'phui-inline-comment-view-css' => '4107254a',
'phui-invisible-character-view-css' => 'c694c4a4',
'phui-left-right-css' => '68513c34',
'phui-lightbox-css' => '4ebf22da',
@@ -1561,9 +1561,6 @@
'phabricator-diff-path-view',
'phuix-button-view',
),
- '6fa445ef' => array(
- 'javelin-dom',
- ),
70245195 => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1642,6 +1639,9 @@
'8207abf9' => array(
'javelin-dom',
),
+ '829b88bf' => array(
+ 'javelin-dom',
+ ),
83754533 => array(
'javelin-install',
'javelin-util',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -3161,6 +3161,7 @@
'PhabricatorDeveloperConfigOptions' => 'applications/config/option/PhabricatorDeveloperConfigOptions.php',
'PhabricatorDeveloperPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDeveloperPreferencesSettingsPanel.php',
'PhabricatorDiffInlineCommentContentState' => 'infrastructure/diff/inline/PhabricatorDiffInlineCommentContentState.php',
+ 'PhabricatorDiffInlineCommentContext' => 'infrastructure/diff/inline/PhabricatorDiffInlineCommentContext.php',
'PhabricatorDiffInlineCommentQuery' => 'infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php',
'PhabricatorDiffPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php',
'PhabricatorDiffScopeEngine' => 'infrastructure/diff/PhabricatorDiffScopeEngine.php',
@@ -3594,6 +3595,7 @@
'PhabricatorInlineComment' => 'infrastructure/diff/interface/PhabricatorInlineComment.php',
'PhabricatorInlineCommentAdjustmentEngine' => 'infrastructure/diff/engine/PhabricatorInlineCommentAdjustmentEngine.php',
'PhabricatorInlineCommentContentState' => 'infrastructure/diff/inline/PhabricatorInlineCommentContentState.php',
+ 'PhabricatorInlineCommentContext' => 'infrastructure/diff/inline/PhabricatorInlineCommentContext.php',
'PhabricatorInlineCommentController' => 'infrastructure/diff/PhabricatorInlineCommentController.php',
'PhabricatorInlineCommentInterface' => 'applications/transactions/interface/PhabricatorInlineCommentInterface.php',
'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php',
@@ -9630,6 +9632,7 @@
'PhabricatorDeveloperConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorDeveloperPreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel',
'PhabricatorDiffInlineCommentContentState' => 'PhabricatorInlineCommentContentState',
+ 'PhabricatorDiffInlineCommentContext' => 'PhabricatorInlineCommentContext',
'PhabricatorDiffInlineCommentQuery' => 'PhabricatorApplicationTransactionCommentQuery',
'PhabricatorDiffPreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel',
'PhabricatorDiffScopeEngine' => 'Phobject',
@@ -10122,6 +10125,7 @@
),
'PhabricatorInlineCommentAdjustmentEngine' => 'Phobject',
'PhabricatorInlineCommentContentState' => 'Phobject',
+ 'PhabricatorInlineCommentContext' => 'Phobject',
'PhabricatorInlineCommentController' => 'PhabricatorController',
'PhabricatorInlineSummaryView' => 'AphrontView',
'PhabricatorInstructionsEditField' => 'PhabricatorEditField',
diff --git a/src/applications/audit/storage/PhabricatorAuditTransactionComment.php b/src/applications/audit/storage/PhabricatorAuditTransactionComment.php
--- a/src/applications/audit/storage/PhabricatorAuditTransactionComment.php
+++ b/src/applications/audit/storage/PhabricatorAuditTransactionComment.php
@@ -17,6 +17,7 @@
protected $attributes = array();
private $replyToComment = self::ATTACHABLE;
+ private $inlineContext = self::ATTACHABLE;
public function getApplicationTransactionObject() {
return new PhabricatorAuditTransaction();
@@ -83,12 +84,18 @@
return $this;
}
- public function isEmptyInlineComment() {
- return !strlen($this->getContent());
- }
-
public function newInlineCommentObject() {
return PhabricatorAuditInlineComment::newFromModernComment($this);
}
+ public function getInlineContext() {
+ return $this->assertAttached($this->inlineContext);
+ }
+
+ public function attachInlineContext(
+ PhabricatorInlineCommentContext $context = null) {
+ $this->inlineContext = $context;
+ return $this;
+ }
+
}
diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php
--- a/src/applications/differential/controller/DifferentialChangesetViewController.php
+++ b/src/applications/differential/controller/DifferentialChangesetViewController.php
@@ -200,6 +200,7 @@
->withPublishableComments(true)
->withPublishedComments(true)
->needHidden(true)
+ ->needInlineContext(true)
->execute();
$inlines = mpull($inlines, 'newInlineCommentObject');
diff --git a/src/applications/differential/editor/DifferentialRevisionEditEngine.php b/src/applications/differential/editor/DifferentialRevisionEditEngine.php
--- a/src/applications/differential/editor/DifferentialRevisionEditEngine.php
+++ b/src/applications/differential/editor/DifferentialRevisionEditEngine.php
@@ -328,6 +328,13 @@
$content = array();
if ($inlines) {
+ // Reload inlines to get inline context.
+ $inlines = id(new DifferentialDiffInlineCommentQuery())
+ ->setViewer($viewer)
+ ->withIDs(mpull($inlines, 'getID'))
+ ->needInlineContext(true)
+ ->execute();
+
$inline_preview = id(new PHUIDiffInlineCommentPreviewListView())
->setViewer($viewer)
->setInlineComments($inlines);
diff --git a/src/applications/differential/storage/DifferentialTransactionComment.php b/src/applications/differential/storage/DifferentialTransactionComment.php
--- a/src/applications/differential/storage/DifferentialTransactionComment.php
+++ b/src/applications/differential/storage/DifferentialTransactionComment.php
@@ -18,6 +18,7 @@
private $replyToComment = self::ATTACHABLE;
private $isHidden = self::ATTACHABLE;
private $changeset = self::ATTACHABLE;
+ private $inlineContext = self::ATTACHABLE;
public function getApplicationTransactionObject() {
return new DifferentialTransaction();
@@ -129,12 +130,18 @@
return $this;
}
- public function isEmptyInlineComment() {
- return !strlen($this->getContent());
- }
-
public function newInlineCommentObject() {
return DifferentialInlineComment::newFromModernComment($this);
}
+ public function getInlineContext() {
+ return $this->assertAttached($this->inlineContext);
+ }
+
+ public function attachInlineContext(
+ PhabricatorInlineCommentContext $context = null) {
+ $this->inlineContext = $context;
+ return $this;
+ }
+
}
diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php
--- a/src/applications/differential/view/DifferentialChangesetListView.php
+++ b/src/applications/differential/view/DifferentialChangesetListView.php
@@ -373,6 +373,9 @@
'Add new inline comment on selected source text.' =>
pht('Add new inline comment on selected source text.'),
+
+ 'Suggest Edit' => pht('Suggest Edit'),
+ 'Discard Edit' => pht('Discard Edit'),
),
));
diff --git a/src/infrastructure/diff/PhabricatorInlineCommentController.php b/src/infrastructure/diff/PhabricatorInlineCommentController.php
--- a/src/infrastructure/diff/PhabricatorInlineCommentController.php
+++ b/src/infrastructure/diff/PhabricatorInlineCommentController.php
@@ -240,7 +240,7 @@
$view = $this->buildScaffoldForView($edit_dialog);
- return $this->newInlineResponse($inline, $view);
+ return $this->newInlineResponse($inline, $view, true);
case 'cancel':
$inline = $this->loadCommentByIDForEdit($this->getCommentID());
@@ -325,6 +325,9 @@
$this->saveComment($inline);
+ // Reload the inline to attach context.
+ $inline = $this->loadCommentByIDForEdit($inline->getID());
+
$edit_dialog = $this->buildEditDialog($inline);
if ($this->getOperation() == 'reply') {
@@ -335,7 +338,7 @@
$view = $this->buildScaffoldForView($edit_dialog);
- return $this->newInlineResponse($inline, $view);
+ return $this->newInlineResponse($inline, $view, true);
}
}
@@ -431,7 +434,7 @@
$view = $this->buildScaffoldForView($view);
- return $this->newInlineResponse($inline, $view);
+ return $this->newInlineResponse($inline, $view, false);
}
private function buildScaffoldForView(PHUIDiffInlineCommentView $view) {
@@ -446,11 +449,29 @@
private function newInlineResponse(
PhabricatorInlineComment $inline,
- $view) {
+ $view,
+ $is_edit) {
+
+ if ($inline->getReplyToCommentPHID()) {
+ $can_suggest = false;
+ } else {
+ $can_suggest = (bool)$inline->getInlineContext();
+ }
+
+ if ($is_edit) {
+ $viewer = $this->getViewer();
+ $content_state = $inline->getContentStateForEdit($viewer);
+ } else {
+ $content_state = $inline->getContentState();
+ }
+
+ $state_map = $content_state->newStorageMap();
$response = array(
'inline' => array(
'id' => $inline->getID(),
+ 'contentState' => $state_map,
+ 'canSuggestEdit' => $can_suggest,
),
'view' => hsprintf('%s', $view),
);
@@ -477,7 +498,8 @@
$viewer = $this->getViewer();
$query = $this->newInlineCommentQuery()
- ->withIDs(array($id));
+ ->withIDs(array($id))
+ ->needInlineContext(true);
$inline = $this->loadCommentByQuery($query);
diff --git a/src/infrastructure/diff/inline/PhabricatorDiffInlineCommentContentState.php b/src/infrastructure/diff/inline/PhabricatorDiffInlineCommentContentState.php
--- a/src/infrastructure/diff/inline/PhabricatorDiffInlineCommentContentState.php
+++ b/src/infrastructure/diff/inline/PhabricatorDiffInlineCommentContentState.php
@@ -12,7 +12,7 @@
}
if ($this->getContentHasSuggestion()) {
- if (strlen($this->getSuggestionText())) {
+ if (strlen($this->getContentSuggestionText())) {
return false;
}
}
diff --git a/src/infrastructure/diff/inline/PhabricatorDiffInlineCommentContext.php b/src/infrastructure/diff/inline/PhabricatorDiffInlineCommentContext.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/diff/inline/PhabricatorDiffInlineCommentContext.php
@@ -0,0 +1,37 @@
+<?php
+
+final class PhabricatorDiffInlineCommentContext
+ extends PhabricatorInlineCommentContext {
+
+ private $headLines;
+ private $bodyLines;
+ private $tailLines;
+
+ public function setHeadLines(array $head_lines) {
+ $this->headLines = $head_lines;
+ return $this;
+ }
+
+ public function getHeadLines() {
+ return $this->headLines;
+ }
+
+ public function setBodyLines(array $body_lines) {
+ $this->bodyLines = $body_lines;
+ return $this;
+ }
+
+ public function getBodyLines() {
+ return $this->bodyLines;
+ }
+
+ public function setTailLines(array $tail_lines) {
+ $this->tailLines = $tail_lines;
+ return $this;
+ }
+
+ public function getTailLines() {
+ return $this->tailLines;
+ }
+
+}
diff --git a/src/infrastructure/diff/inline/PhabricatorInlineCommentContext.php b/src/infrastructure/diff/inline/PhabricatorInlineCommentContext.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/diff/inline/PhabricatorInlineCommentContext.php
@@ -0,0 +1,4 @@
+<?php
+
+abstract class PhabricatorInlineCommentContext
+ extends Phobject {}
diff --git a/src/infrastructure/diff/interface/PhabricatorInlineComment.php b/src/infrastructure/diff/interface/PhabricatorInlineComment.php
--- a/src/infrastructure/diff/interface/PhabricatorInlineComment.php
+++ b/src/infrastructure/diff/interface/PhabricatorInlineComment.php
@@ -350,6 +350,10 @@
return $this;
}
+ public function getInlineContext() {
+ return $this->getStorageObject()->getInlineContext();
+ }
+
/* -( PhabricatorMarkupInterface Implementation )-------------------------- */
diff --git a/src/infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php b/src/infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php
--- a/src/infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php
+++ b/src/infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php
@@ -9,6 +9,7 @@
private $publishableComments;
private $needHidden;
private $needAppliedDrafts;
+ private $needInlineContext;
abstract protected function buildInlineCommentWhereClauseParts(
AphrontDatabaseConnection $conn);
@@ -42,6 +43,11 @@
return $this;
}
+ final public function needInlineContext($need_context) {
+ $this->needInlineContext = $need_context;
+ return $this;
+ }
+
final public function needAppliedDrafts($need_applied) {
$this->needAppliedDrafts = $need_applied;
return $this;
@@ -173,26 +179,6 @@
return $inlines;
}
- if ($this->needHidden) {
- $viewer_phid = $viewer->getPHID();
-
- if ($viewer_phid) {
- $hidden = $this->loadHiddenCommentIDs(
- $viewer_phid,
- $inlines);
- } else {
- $hidden = array();
- }
-
- foreach ($inlines as $inline) {
- $inline->attachIsHidden(isset($hidden[$inline->getID()]));
- }
- }
-
- if (!$inlines) {
- return $inlines;
- }
-
$need_drafts = $this->needAppliedDrafts;
$drop_void = $this->publishableComments;
$convert_objects = ($need_drafts || $drop_void);
@@ -247,4 +233,133 @@
return $inlines;
}
+ protected function didFilterPage(array $inlines) {
+ $viewer = $this->getViewer();
+
+ if ($this->needHidden) {
+ $viewer_phid = $viewer->getPHID();
+
+ if ($viewer_phid) {
+ $hidden = $this->loadHiddenCommentIDs(
+ $viewer_phid,
+ $inlines);
+ } else {
+ $hidden = array();
+ }
+
+ foreach ($inlines as $inline) {
+ $inline->attachIsHidden(isset($hidden[$inline->getID()]));
+ }
+ }
+
+ if ($this->needInlineContext) {
+ $need_context = array();
+ foreach ($inlines as $inline) {
+ $object = $inline->newInlineCommentObject();
+
+ if ($object->getDocumentEngineKey() !== null) {
+ $inline->attachInlineContext(null);
+ continue;
+ }
+
+ $need_context[] = $inline;
+ }
+
+ foreach ($need_context as $inline) {
+ $changeset = id(new DifferentialChangesetQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($inline->getChangesetID()))
+ ->needHunks(true)
+ ->executeOne();
+ if (!$changeset) {
+ $inline->attachInlineContext(null);
+ continue;
+ }
+
+ $hunks = $changeset->getHunks();
+
+ $is_simple =
+ (count($hunks) === 1) &&
+ ((int)head($hunks)->getOldOffset() <= 1) &&
+ ((int)head($hunks)->getNewOffset() <= 1);
+
+ if (!$is_simple) {
+ $inline->attachInlineContext(null);
+ continue;
+ }
+
+ if ($inline->getIsNewFile()) {
+ $corpus = $changeset->makeNewFile();
+ } else {
+ $corpus = $changeset->makeOldFile();
+ }
+
+ $corpus = phutil_split_lines($corpus);
+
+ // Adjust the line number into a 0-based offset.
+ $offset = $inline->getLineNumber();
+ $offset = $offset - 1;
+
+ // Adjust the inclusive range length into a row count.
+ $length = $inline->getLineLength();
+ $length = $length + 1;
+
+ $head_min = max(0, $offset - 3);
+ $head_max = $offset;
+ $head_len = $head_max - $head_min;
+
+ if ($head_len) {
+ $head = array_slice($corpus, $head_min, $head_len, true);
+ $head = $this->simplifyContext($head, true);
+ } else {
+ $head = array();
+ }
+
+ $body = array_slice($corpus, $offset, $length, true);
+
+ $tail = array_slice($corpus, $offset + $length, 3, true);
+ $tail = $this->simplifyContext($tail, false);
+
+ $context = id(new PhabricatorDiffInlineCommentContext())
+ ->setHeadLines($head)
+ ->setBodyLines($body)
+ ->setTailLines($tail);
+
+ $inline->attachInlineContext($context);
+ }
+
+ }
+
+ return $inlines;
+ }
+
+ private function simplifyContext(array $lines, $is_head) {
+ // We want to provide the smallest amount of context we can while still
+ // being useful, since the actual code is visible nearby and showing a
+ // ton of context is silly.
+
+ // Examine each line until we find one that looks "useful" (not just
+ // whitespace or a single bracket). Once we find a useful piece of context
+ // to anchor the text, discard the rest of the lines beyond it.
+
+ if ($is_head) {
+ $lines = array_reverse($lines, true);
+ }
+
+ $saw_context = false;
+ foreach ($lines as $key => $line) {
+ if ($saw_context) {
+ unset($lines[$key]);
+ continue;
+ }
+
+ $saw_context = (strlen(trim($line)) > 3);
+ }
+
+ if ($is_head) {
+ $lines = array_reverse($lines, true);
+ }
+
+ return $lines;
+ }
}
diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php
--- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php
+++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php
@@ -427,6 +427,15 @@
$metadata['menuItems'] = $menu_items;
+ $suggestion_content = $this->newSuggestionView($inline);
+
+ $inline_content = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'phabricator-remarkup',
+ ),
+ $content);
+
$markup = javelin_tag(
'div',
array(
@@ -445,9 +454,15 @@
$group_left,
$group_right,
)),
- phutil_tag_div(
- 'differential-inline-comment-content',
- phutil_tag_div('phabricator-remarkup', $content)),
+ phutil_tag(
+ 'div',
+ array(
+ 'class' => 'differential-inline-comment-content',
+ ),
+ array(
+ $suggestion_content,
+ $inline_content,
+ )),
));
$summary = phutil_tag(
@@ -491,4 +506,57 @@
return true;
}
+ private function newSuggestionView(PhabricatorInlineComment $inline) {
+ $content_state = $inline->getContentState();
+ if (!$content_state->getContentHasSuggestion()) {
+ return null;
+ }
+
+ $context = $inline->getInlineContext();
+ if (!$context) {
+ return null;
+ }
+
+ $head_lines = $context->getHeadLines();
+ $head_lines = implode('', $head_lines);
+
+ $tail_lines = $context->getTailLines();
+ $tail_lines = implode('', $tail_lines);
+
+ $old_lines = $context->getBodyLines();
+ $old_lines = implode('', $old_lines);
+ $old_lines = $head_lines.$old_lines.$tail_lines;
+ if (strlen($old_lines) && !preg_match('/\n\z/', $old_lines)) {
+ $old_lines .= "\n";
+ }
+
+ $new_lines = $content_state->getContentSuggestionText();
+ $new_lines = $head_lines.$new_lines.$tail_lines;
+ if (strlen($new_lines) && !preg_match('/\n\z/', $new_lines)) {
+ $new_lines .= "\n";
+ }
+
+ if ($old_lines === $new_lines) {
+ return null;
+ }
+
+
+ $raw_diff = id(new PhabricatorDifferenceEngine())
+ ->generateRawDiffFromFileContent($old_lines, $new_lines);
+
+ $raw_diff = phutil_split_lines($raw_diff);
+ $raw_diff = array_slice($raw_diff, 3);
+ $raw_diff = implode('', $raw_diff);
+
+ $view = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'inline-suggestion-view PhabricatorMonospaced',
+ ),
+ $raw_diff);
+
+ return $view;
+ }
+
+
}
diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php
--- a/src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php
+++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php
@@ -46,17 +46,23 @@
),
$this->title);
+ $corpus_view = $this->newCorpusView();
+
$body = phutil_tag(
'div',
array(
'class' => 'differential-inline-comment-edit-body',
),
- $this->newTextarea());
+ array(
+ $corpus_view,
+ $this->newTextarea(),
+ ));
- $edit = phutil_tag(
+ $edit = javelin_tag(
'div',
array(
'class' => 'differential-inline-comment-edit-buttons grouped',
+ 'sigil' => 'inline-edit-buttons',
),
array(
$buttons,
@@ -91,4 +97,121 @@
->setDisableFullScreen(true);
}
+ private function newCorpusView() {
+ $viewer = $this->getViewer();
+ $inline = $this->getInlineComment();
+
+ $context = $inline->getInlineContext();
+ if ($context === null) {
+ return null;
+ }
+
+ $head = $context->getHeadLines();
+ $head = $this->newContextView($head);
+
+ $state = $inline->getContentStateForEdit($viewer);
+
+ $main = $state->getContentSuggestionText();
+ $main_count = count(phutil_split_lines($main));
+
+ $default = $context->getBodyLines();
+ $default = implode('', $default);
+
+ // Browsers ignore one leading newline in text areas. Add one so that
+ // any actual leading newlines in the content are preserved.
+ $main = "\n".$main;
+
+ $textarea = javelin_tag(
+ 'textarea',
+ array(
+ 'class' => 'inline-suggestion-input PhabricatorMonospaced',
+ 'rows' => max(3, $main_count + 1),
+ 'sigil' => 'inline-content-suggestion',
+ 'meta' => array(
+ 'defaultText' => $default,
+ ),
+ ),
+ $main);
+
+ $main = phutil_tag(
+ 'tr',
+ array(
+ 'class' => 'inline-suggestion-input-row',
+ ),
+ array(
+ phutil_tag(
+ 'td',
+ array(
+ 'class' => 'inline-suggestion-line-cell',
+ ),
+ null),
+ phutil_tag(
+ 'td',
+ array(
+ 'class' => 'inline-suggestion-input-cell',
+ ),
+ $textarea),
+ ));
+
+ $tail = $context->getTailLines();
+ $tail = $this->newContextView($tail);
+
+ $body = phutil_tag(
+ 'tbody',
+ array(),
+ array(
+ $head,
+ $main,
+ $tail,
+ ));
+
+ $table = phutil_tag(
+ 'table',
+ array(
+ 'class' => 'inline-suggestion-table',
+ ),
+ $body);
+
+ $container = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'inline-suggestion',
+ ),
+ $table);
+
+ return $container;
+ }
+
+ private function newContextView(array $lines) {
+ if (!$lines) {
+ return array();
+ }
+
+ $rows = array();
+ foreach ($lines as $index => $line) {
+ $line_cell = phutil_tag(
+ 'td',
+ array(
+ 'class' => 'inline-suggestion-line-cell PhabricatorMonospaced',
+ ),
+ $index + 1);
+
+ $text_cell = phutil_tag(
+ 'td',
+ array(
+ 'class' => 'inline-suggestion-text-cell PhabricatorMonospaced',
+ ),
+ $line);
+
+ $cells = array(
+ $line_cell,
+ $text_cell,
+ );
+
+ $rows[] = phutil_tag('tr', array(), $cells);
+ }
+
+ return $rows;
+ }
+
}
diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentView.php
--- a/src/infrastructure/diff/view/PHUIDiffInlineCommentView.php
+++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentView.php
@@ -93,7 +93,7 @@
'startOffset' => $inline->getStartOffset(),
'endOffset' => $inline->getEndOffset(),
'on_right' => $this->getIsOnRight(),
- 'contentState' => $inline->getContentState(),
+ 'contentState' => $inline->getContentState()->newStorageMap(),
);
}
diff --git a/webroot/rsrc/css/application/differential/phui-inline-comment.css b/webroot/rsrc/css/application/differential/phui-inline-comment.css
--- a/webroot/rsrc/css/application/differential/phui-inline-comment.css
+++ b/webroot/rsrc/css/application/differential/phui-inline-comment.css
@@ -436,3 +436,60 @@
background: {$lightyellow};
border-color: {$yellow};
}
+
+.inline-suggestion {
+ display: none;
+ margin: 0 -8px;
+}
+
+.has-suggestion .inline-suggestion {
+ display: block;
+}
+
+.differential-inline-comment-edit-buttons button.inline-button-left {
+ float: left;
+ margin: 0 6px 0 0;
+}
+
+.inline-suggestion-table {
+ table-layout: fixed;
+ width: 100%;
+ margin-bottom: 8px;
+ white-space: pre-wrap;
+ background: {$greybackground};
+ border-width: 1px 0;
+ border-style: solid;
+ border-color: {$lightgreyborder};
+}
+
+textarea.inline-suggestion-input {
+ width: 100%;
+ height: auto;
+ max-width: 100%;
+}
+
+.inline-suggestion-line-cell {
+ text-align: right;
+ background: {$darkgreybackground};
+ width: 36px;
+ color: {$greytext};
+ border-right: 1px solid {$lightgreyborder};
+}
+
+.inline-suggestion-input-cell {
+ padding: 8px;
+}
+
+.inline-suggestion-text-cell {
+ padding: 0 8px;
+}
+
+.inline-suggestion-view {
+ padding: 8px 12px;
+ white-space: pre-wrap;
+ background: {$greybackground};
+ margin: 0 -12px 8px;
+ border-width: 1px 0;
+ border-style: solid;
+ border-color: {$lightgreyborder};
+}
diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js
--- a/webroot/rsrc/js/application/diff/DiffInline.js
+++ b/webroot/rsrc/js/application/diff/DiffInline.js
@@ -51,6 +51,7 @@
_startOffset: null,
_endOffset: null,
_isSelected: false,
+ _canSuggestEdit: false,
bindToRow: function(row) {
this._row = row;
@@ -76,7 +77,6 @@
this._number = parseInt(data.number, 10);
this._length = parseInt(data.length, 10);
- this._originalState = data.contentState;
this._isNewFile = data.isNewFile;
this._replyToCommentPHID = data.replyToCommentPHID;
@@ -602,6 +602,8 @@
_readInlineState: function(state) {
this._id = state.id;
+ this._originalState = state.contentState;
+ this._canSuggestEdit = state.canSuggestEdit;
},
_ondeleteresponse: function() {
@@ -664,6 +666,11 @@
_drawEditRows: function(rows) {
this.setEditing(true);
this._editRow = this._drawRows(rows, null, 'edit');
+
+ this._drawSuggestionState(this._editRow);
+ JX.log(this._originalState);
+
+ this.setHasSuggestion(this._originalState.hasSuggestion);
},
_drawRows: function(rows, cursor, type) {
@@ -719,6 +726,91 @@
return result_row;
},
+ _drawSuggestionState: function(row) {
+ if (this._canSuggestEdit) {
+ var button = this._getSuggestionButton();
+ var node = button.getNode();
+
+ // As a side effect of form submission, the button may become
+ // visually disabled. Re-enable it. This is a bit hacky.
+ JX.DOM.alterClass(node, 'disabled', false);
+ node.disabled = false;
+
+ var container = JX.DOM.find(row, 'div', 'inline-edit-buttons');
+ container.appendChild(node);
+ }
+ },
+
+ _getSuggestionButton: function() {
+ if (!this._suggestionButton) {
+ var button = new JX.PHUIXButtonView()
+ .setIcon('fa-pencil-square-o')
+ .setColor('grey');
+
+ var node = button.getNode();
+ JX.DOM.alterClass(node, 'inline-button-left', true);
+
+ var onclick = JX.bind(this, this._onSuggestEdit);
+ JX.DOM.listen(node, 'click', null, onclick);
+
+ this._suggestionButton = button;
+ }
+
+ return this._suggestionButton;
+ },
+
+ _onSuggestEdit: function(e) {
+ e.kill();
+
+ this.setHasSuggestion(!this.getHasSuggestion());
+
+ // The first time the user actually clicks the button and enables
+ // suggestions for a given editor state, fill the input with the
+ // underlying text if there isn't any text yet.
+ if (this.getHasSuggestion()) {
+ if (this._editRow) {
+ var node = this._getSuggestionNode(this._editRow);
+ if (node) {
+ if (!node.value.length) {
+ var data = JX.Stratcom.getData(node);
+ if (!data.hasSetDefault) {
+ data.hasSetDefault = true;
+ node.value = data.defaultText;
+ node.rows = Math.max(3, node.value.split('\n').length);
+ }
+ }
+ }
+ }
+ }
+
+ // Save the "hasSuggestion" part of the content state.
+ this.triggerDraft();
+ },
+
+ setHasSuggestion: function(has_suggestion) {
+ this._hasSuggestion = has_suggestion;
+
+ var button = this._getSuggestionButton();
+ var pht = this.getChangeset().getChangesetList().getTranslations();
+ if (has_suggestion) {
+ button
+ .setIcon('fa-times')
+ .setText(pht('Discard Edit'));
+ } else {
+ button
+ .setIcon('fa-plus')
+ .setText(pht('Suggest Edit'));
+ }
+
+ if (this._editRow) {
+ JX.DOM.alterClass(this._editRow, 'has-suggestion', has_suggestion);
+ }
+ },
+
+ getHasSuggestion: function() {
+ return this._hasSuggestion;
+ },
+
save: function() {
var handler = JX.bind(this, this._onsubmitresponse);
@@ -825,16 +917,24 @@
// Ignore.
}
- try {
- node = JX.DOM.find(row, 'textarea', 'inline-content-suggestion');
+ node = this._getSuggestionNode(row);
+ if (node) {
state.suggestionText = node.value;
- } catch (ex) {
- // Ignore.
}
+ state.hasSuggestion = this.getHasSuggestion();
+
return state;
},
+ _getSuggestionNode: function(row) {
+ try {
+ return JX.DOM.find(row, 'textarea', 'inline-content-suggestion');
+ } catch (ex) {
+ return null;
+ }
+ },
+
_onsubmitresponse: function(response) {
if (this._editRow) {
JX.DOM.remove(this._editRow);
@@ -1063,7 +1163,7 @@
return {
text: '',
suggestionText: '',
- hasSuggestion: true
+ hasSuggestion: false
};
},
@@ -1073,6 +1173,7 @@
_isSameContentState: function(u, v) {
return (
+ ((u === null) === (v === null)) &&
(u.text === v.text) &&
(u.suggestionText === v.suggestionText) &&
(u.hasSuggestion === v.hasSuggestion));

File Metadata

Mime Type
text/plain
Expires
Wed, Apr 2, 7:43 PM (1 w, 2 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/6y/p2/bepaguib74ku6z35
Default Alt Text
D21276.id50661.diff (32 KB)

Event Timeline