mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Substantially support character encodings and "Highlight As" in changesets
Summary: Ref T5179. Ref T4045. Ref T832. We can now write non-utf8 hunks into the database, so try to do more reasonable things with them in the UI. Test Plan: (See screenshots...) Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T832, T4045, T5179 Differential Revision: https://secure.phabricator.com/D9294
This commit is contained in:
parent
5660684d7f
commit
b20884a842
16 changed files with 626 additions and 306 deletions
|
@ -11,7 +11,7 @@ return array(
|
|||
'core.pkg.js' => '07b01d4f',
|
||||
'darkconsole.pkg.js' => 'ca8671ce',
|
||||
'differential.pkg.css' => '4a93db37',
|
||||
'differential.pkg.js' => 'eca39a2c',
|
||||
'differential.pkg.js' => '2b128f3a',
|
||||
'diffusion.pkg.css' => '471bc9eb',
|
||||
'diffusion.pkg.js' => '077e3ad0',
|
||||
'maniphest.pkg.css' => 'f88a8402',
|
||||
|
@ -358,13 +358,13 @@ return array(
|
|||
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => 'fa187a68',
|
||||
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '3be3eef5',
|
||||
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'aa077691',
|
||||
'rsrc/js/application/differential/ChangesetViewManager.js' => 'db09a523',
|
||||
'rsrc/js/application/differential/ChangesetViewManager.js' => '1f304ef8',
|
||||
'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => 'f2441746',
|
||||
'rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js' => '533a187b',
|
||||
'rsrc/js/application/differential/behavior-comment-jump.js' => '71755c79',
|
||||
'rsrc/js/application/differential/behavior-comment-preview.js' => '127f2018',
|
||||
'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1',
|
||||
'rsrc/js/application/differential/behavior-dropdown-menus.js' => '64a79839',
|
||||
'rsrc/js/application/differential/behavior-dropdown-menus.js' => '710f209e',
|
||||
'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '00861799',
|
||||
'rsrc/js/application/differential/behavior-keyboard-nav.js' => '173ce7e7',
|
||||
'rsrc/js/application/differential/behavior-populate.js' => 'bdb3e4d0',
|
||||
|
@ -509,7 +509,7 @@ return array(
|
|||
'aphront-two-column-view-css' => '16ab3ad2',
|
||||
'aphront-typeahead-control-css' => 'a989b5b3',
|
||||
'auth-css' => '1e655982',
|
||||
'changeset-view-manager' => 'db09a523',
|
||||
'changeset-view-manager' => '1f304ef8',
|
||||
'config-options-css' => '7fedf08b',
|
||||
'conpherence-menu-css' => 'e1e0fdf1',
|
||||
'conpherence-message-pane-css' => '11a393ca',
|
||||
|
@ -564,7 +564,7 @@ return array(
|
|||
'javelin-behavior-differential-add-reviewers-and-ccs' => '533a187b',
|
||||
'javelin-behavior-differential-comment-jump' => '71755c79',
|
||||
'javelin-behavior-differential-diff-radios' => 'e1ff79b1',
|
||||
'javelin-behavior-differential-dropdown-menus' => '64a79839',
|
||||
'javelin-behavior-differential-dropdown-menus' => '710f209e',
|
||||
'javelin-behavior-differential-edit-inline-comments' => '00861799',
|
||||
'javelin-behavior-differential-feedback-preview' => '127f2018',
|
||||
'javelin-behavior-differential-keyboard-navigation' => '173ce7e7',
|
||||
|
@ -1001,6 +1001,17 @@ return array(
|
|||
5 => 'phabricator-drag-and-drop-file-upload',
|
||||
6 => 'phabricator-draggable-list',
|
||||
),
|
||||
'1f304ef8' =>
|
||||
array(
|
||||
0 => 'javelin-dom',
|
||||
1 => 'javelin-util',
|
||||
2 => 'javelin-stratcom',
|
||||
3 => 'javelin-install',
|
||||
4 => 'javelin-workflow',
|
||||
5 => 'javelin-router',
|
||||
6 => 'javelin-behavior-device',
|
||||
7 => 'javelin-vector',
|
||||
),
|
||||
'2290aeef' =>
|
||||
array(
|
||||
0 => 'javelin-install',
|
||||
|
@ -1273,11 +1284,6 @@ return array(
|
|||
2 => 'javelin-util',
|
||||
3 => 'phabricator-shaped-request',
|
||||
),
|
||||
'7319e029' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
),
|
||||
'62e18640' =>
|
||||
array(
|
||||
0 => 'javelin-install',
|
||||
|
@ -1291,18 +1297,6 @@ return array(
|
|||
1 => 'javelin-dom',
|
||||
2 => 'javelin-fx',
|
||||
),
|
||||
'64a79839' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
3 => 'javelin-stratcom',
|
||||
4 => 'phuix-dropdown-menu',
|
||||
5 => 'phuix-action-list-view',
|
||||
6 => 'phuix-action-view',
|
||||
7 => 'phabricator-phtize',
|
||||
8 => 'changeset-view-manager',
|
||||
),
|
||||
'64ef2fd2' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
|
@ -1344,12 +1338,30 @@ return array(
|
|||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
),
|
||||
'710f209e' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
3 => 'javelin-stratcom',
|
||||
4 => 'javelin-workflow',
|
||||
5 => 'phuix-dropdown-menu',
|
||||
6 => 'phuix-action-list-view',
|
||||
7 => 'phuix-action-view',
|
||||
8 => 'phabricator-phtize',
|
||||
9 => 'changeset-view-manager',
|
||||
),
|
||||
'71755c79' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-stratcom',
|
||||
2 => 'javelin-dom',
|
||||
),
|
||||
'7319e029' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
),
|
||||
'76f4ebed' =>
|
||||
array(
|
||||
0 => 'javelin-install',
|
||||
|
@ -1916,17 +1928,6 @@ return array(
|
|||
1 => 'javelin-util',
|
||||
2 => 'javelin-stratcom',
|
||||
),
|
||||
'db09a523' =>
|
||||
array(
|
||||
0 => 'javelin-dom',
|
||||
1 => 'javelin-util',
|
||||
2 => 'javelin-stratcom',
|
||||
3 => 'javelin-install',
|
||||
4 => 'javelin-workflow',
|
||||
5 => 'javelin-router',
|
||||
6 => 'javelin-behavior-device',
|
||||
7 => 'javelin-vector',
|
||||
),
|
||||
'dd7e8ef5' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
|
|
|
@ -2258,6 +2258,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSystemRemoveDestroyWorkflow' => 'applications/system/management/PhabricatorSystemRemoveDestroyWorkflow.php',
|
||||
'PhabricatorSystemRemoveLogWorkflow' => 'applications/system/management/PhabricatorSystemRemoveLogWorkflow.php',
|
||||
'PhabricatorSystemRemoveWorkflow' => 'applications/system/management/PhabricatorSystemRemoveWorkflow.php',
|
||||
'PhabricatorSystemSelectEncodingController' => 'applications/system/controller/PhabricatorSystemSelectEncodingController.php',
|
||||
'PhabricatorSystemSelectHighlightController' => 'applications/system/controller/PhabricatorSystemSelectHighlightController.php',
|
||||
'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/PhabricatorTaskmasterDaemon.php',
|
||||
'PhabricatorTestCase' => 'infrastructure/testing/PhabricatorTestCase.php',
|
||||
'PhabricatorTestController' => 'applications/base/controller/__tests__/PhabricatorTestController.php',
|
||||
|
@ -5134,6 +5136,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSystemRemoveDestroyWorkflow' => 'PhabricatorSystemRemoveWorkflow',
|
||||
'PhabricatorSystemRemoveLogWorkflow' => 'PhabricatorSystemRemoveWorkflow',
|
||||
'PhabricatorSystemRemoveWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorSystemSelectEncodingController' => 'PhabricatorController',
|
||||
'PhabricatorSystemSelectHighlightController' => 'PhabricatorController',
|
||||
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
|
||||
'PhabricatorTestCase' => 'ArcanistPhutilTestCase',
|
||||
'PhabricatorTestController' => 'PhabricatorController',
|
||||
|
|
|
@ -155,6 +155,8 @@ final class DifferentialChangesetViewController extends DifferentialController {
|
|||
$parser->setRightSideCommentMapping($right_source, $right_new);
|
||||
$parser->setLeftSideCommentMapping($left_source, $left_new);
|
||||
$parser->setWhitespaceMode($request->getStr('whitespace'));
|
||||
$parser->setCharacterEncoding($request->getStr('encoding'));
|
||||
$parser->setHighlightAs($request->getStr('highlight'));
|
||||
|
||||
if ($request->getStr('renderer') == '1up') {
|
||||
$parser->setRenderer(new DifferentialChangesetOneUpRenderer());
|
||||
|
|
|
@ -41,6 +41,26 @@ final class DifferentialChangesetParser {
|
|||
private $highlightErrors;
|
||||
private $disableCache;
|
||||
private $renderer;
|
||||
private $characterEncoding;
|
||||
private $highlightAs;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function setRenderer($renderer) {
|
||||
$this->renderer = $renderer;
|
||||
|
@ -422,8 +442,15 @@ final class DifferentialChangesetParser {
|
|||
}
|
||||
|
||||
private function getHighlightFuture($corpus) {
|
||||
$language = $this->highlightAs;
|
||||
|
||||
if (!$language) {
|
||||
$language = $this->highlightEngine->getLanguageFromFilename(
|
||||
$this->filename);
|
||||
}
|
||||
|
||||
return $this->highlightEngine->getHighlightFuture(
|
||||
$this->highlightEngine->getLanguageFromFilename($this->filename),
|
||||
$language,
|
||||
$corpus);
|
||||
}
|
||||
|
||||
|
@ -455,6 +482,14 @@ final class DifferentialChangesetParser {
|
|||
$skip_cache = true;
|
||||
}
|
||||
|
||||
if ($this->characterEncoding) {
|
||||
$skip_cache = true;
|
||||
}
|
||||
|
||||
if ($this->highlightAs) {
|
||||
$skip_cache = true;
|
||||
}
|
||||
|
||||
$this->whitespaceMode = $whitespace_mode;
|
||||
|
||||
$changeset = $this->changeset;
|
||||
|
@ -677,6 +712,25 @@ final class DifferentialChangesetParser {
|
|||
// requests.
|
||||
$this->isTopLevel = (($range_start === null) && ($range_len === null));
|
||||
$this->highlightEngine = PhabricatorSyntaxHighlighter::newEngine();
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
$this->tryCacheStuff();
|
||||
$render_pch = $this->shouldRenderPropertyChangeHeader($this->changeset);
|
||||
|
||||
|
@ -700,7 +754,8 @@ final class DifferentialChangesetParser {
|
|||
->setMarkupEngine($this->markupEngine)
|
||||
->setHandles($this->handles)
|
||||
->setOldLines($this->old)
|
||||
->setNewLines($this->new);
|
||||
->setNewLines($this->new)
|
||||
->setOriginalCharacterEncoding($encoding);
|
||||
|
||||
if ($this->user) {
|
||||
$renderer->setUser($this->user);
|
||||
|
|
|
@ -9,44 +9,39 @@ abstract class DifferentialChangesetHTMLRenderer
|
|||
$change = $changeset->getChangeType();
|
||||
$file = $changeset->getFileType();
|
||||
|
||||
$message = null;
|
||||
if ($change == DifferentialChangeType::TYPE_CHANGE &&
|
||||
$file == DifferentialChangeType::FILE_TEXT) {
|
||||
if ($force) {
|
||||
// We have to force something to render because there were no changes
|
||||
// of other kinds.
|
||||
$message = pht('This file was not modified.');
|
||||
} else {
|
||||
// Default case of changes to a text file, no metadata.
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$messages = array();
|
||||
$none = hsprintf('');
|
||||
switch ($change) {
|
||||
|
||||
case DifferentialChangeType::TYPE_ADD:
|
||||
switch ($file) {
|
||||
case DifferentialChangeType::FILE_TEXT:
|
||||
$message = pht('This file was <strong>added</strong>.', $none);
|
||||
$messages[] = pht(
|
||||
'This file was <strong>added</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_IMAGE:
|
||||
$message = pht('This image was <strong>added</strong>.', $none);
|
||||
$messages[] = pht(
|
||||
'This image was <strong>added</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_DIRECTORY:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This directory was <strong>added</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_BINARY:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This binary file was <strong>added</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SYMLINK:
|
||||
$message = pht('This symlink was <strong>added</strong>.', $none);
|
||||
$messages[] = pht(
|
||||
'This symlink was <strong>added</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SUBMODULE:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This submodule was <strong>added</strong>.',
|
||||
$none);
|
||||
break;
|
||||
|
@ -56,28 +51,32 @@ abstract class DifferentialChangesetHTMLRenderer
|
|||
case DifferentialChangeType::TYPE_DELETE:
|
||||
switch ($file) {
|
||||
case DifferentialChangeType::FILE_TEXT:
|
||||
$message = pht('This file was <strong>deleted</strong>.', $none);
|
||||
$messages[] = pht(
|
||||
'This file was <strong>deleted</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_IMAGE:
|
||||
$message = pht('This image was <strong>deleted</strong>.', $none);
|
||||
$messages[] = pht(
|
||||
'This image was <strong>deleted</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_DIRECTORY:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This directory was <strong>deleted</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_BINARY:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This binary file was <strong>deleted</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SYMLINK:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This symlink was <strong>deleted</strong>.',
|
||||
$none);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SUBMODULE:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This submodule was <strong>deleted</strong>.',
|
||||
$none);
|
||||
break;
|
||||
|
@ -88,22 +87,22 @@ abstract class DifferentialChangesetHTMLRenderer
|
|||
$from = phutil_tag('strong', array(), $changeset->getOldFile());
|
||||
switch ($file) {
|
||||
case DifferentialChangeType::FILE_TEXT:
|
||||
$message = pht('This file was moved from %s.', $from);
|
||||
$messages[] = pht('This file was moved from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_IMAGE:
|
||||
$message = pht('This image was moved from %s.', $from);
|
||||
$messages[] = pht('This image was moved from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_DIRECTORY:
|
||||
$message = pht('This directory was moved from %s.', $from);
|
||||
$messages[] = pht('This directory was moved from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_BINARY:
|
||||
$message = pht('This binary file was moved from %s.', $from);
|
||||
$messages[] = pht('This binary file was moved from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SYMLINK:
|
||||
$message = pht('This symlink was moved from %s.', $from);
|
||||
$messages[] = pht('This symlink was moved from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SUBMODULE:
|
||||
$message = pht('This submodule was moved from %s.', $from);
|
||||
$messages[] = pht('This submodule was moved from %s.', $from);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -112,22 +111,22 @@ abstract class DifferentialChangesetHTMLRenderer
|
|||
$from = phutil_tag('strong', array(), $changeset->getOldFile());
|
||||
switch ($file) {
|
||||
case DifferentialChangeType::FILE_TEXT:
|
||||
$message = pht('This file was copied from %s.', $from);
|
||||
$messages[] = pht('This file was copied from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_IMAGE:
|
||||
$message = pht('This image was copied from %s.', $from);
|
||||
$messages[] = pht('This image was copied from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_DIRECTORY:
|
||||
$message = pht('This directory was copied from %s.', $from);
|
||||
$messages[] = pht('This directory was copied from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_BINARY:
|
||||
$message = pht('This binary file was copied from %s.', $from);
|
||||
$messages[] = pht('This binary file was copied from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SYMLINK:
|
||||
$message = pht('This symlink was copied from %s.', $from);
|
||||
$messages[] = pht('This symlink was copied from %s.', $from);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SUBMODULE:
|
||||
$message = pht('This submodule was copied from %s.', $from);
|
||||
$messages[] = pht('This submodule was copied from %s.', $from);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -139,22 +138,22 @@ abstract class DifferentialChangesetHTMLRenderer
|
|||
implode(', ', $changeset->getAwayPaths()));
|
||||
switch ($file) {
|
||||
case DifferentialChangeType::FILE_TEXT:
|
||||
$message = pht('This file was moved to %s.', $paths);
|
||||
$messages[] = pht('This file was moved to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_IMAGE:
|
||||
$message = pht('This image was moved to %s.', $paths);
|
||||
$messages[] = pht('This image was moved to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_DIRECTORY:
|
||||
$message = pht('This directory was moved to %s.', $paths);
|
||||
$messages[] = pht('This directory was moved to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_BINARY:
|
||||
$message = pht('This binary file was moved to %s.', $paths);
|
||||
$messages[] = pht('This binary file was moved to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SYMLINK:
|
||||
$message = pht('This symlink was moved to %s.', $paths);
|
||||
$messages[] = pht('This symlink was moved to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SUBMODULE:
|
||||
$message = pht('This submodule was moved to %s.', $paths);
|
||||
$messages[] = pht('This submodule was moved to %s.', $paths);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -166,22 +165,22 @@ abstract class DifferentialChangesetHTMLRenderer
|
|||
implode(', ', $changeset->getAwayPaths()));
|
||||
switch ($file) {
|
||||
case DifferentialChangeType::FILE_TEXT:
|
||||
$message = pht('This file was copied to %s.', $paths);
|
||||
$messages[] = pht('This file was copied to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_IMAGE:
|
||||
$message = pht('This image was copied to %s.', $paths);
|
||||
$messages[] = pht('This image was copied to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_DIRECTORY:
|
||||
$message = pht('This directory was copied to %s.', $paths);
|
||||
$messages[] = pht('This directory was copied to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_BINARY:
|
||||
$message = pht('This binary file was copied to %s.', $paths);
|
||||
$messages[] = pht('This binary file was copied to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SYMLINK:
|
||||
$message = pht('This symlink was copied to %s.', $paths);
|
||||
$messages[] = pht('This symlink was copied to %s.', $paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SUBMODULE:
|
||||
$message = pht('This submodule was copied to %s.', $paths);
|
||||
$messages[] = pht('This submodule was copied to %s.', $paths);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -193,32 +192,32 @@ abstract class DifferentialChangesetHTMLRenderer
|
|||
implode(', ', $changeset->getAwayPaths()));
|
||||
switch ($file) {
|
||||
case DifferentialChangeType::FILE_TEXT:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This file was deleted after being copied to %s.',
|
||||
$paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_IMAGE:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This image was deleted after being copied to %s.',
|
||||
$paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_DIRECTORY:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This directory was deleted after being copied to %s.',
|
||||
$paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_BINARY:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This binary file was deleted after being copied to %s.',
|
||||
$paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SYMLINK:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This symlink was deleted after being copied to %s.',
|
||||
$paths);
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SUBMODULE:
|
||||
$message = pht(
|
||||
$messages[] = pht(
|
||||
'This submodule was deleted after being copied to %s.',
|
||||
$paths);
|
||||
break;
|
||||
|
@ -228,29 +227,57 @@ abstract class DifferentialChangesetHTMLRenderer
|
|||
default:
|
||||
switch ($file) {
|
||||
case DifferentialChangeType::FILE_TEXT:
|
||||
$message = pht('This is a file.');
|
||||
// This is the default case, so we only render this header if
|
||||
// forced to since it's not very useful.
|
||||
if ($force) {
|
||||
$messages[] = pht('This file was not modified.');
|
||||
}
|
||||
break;
|
||||
case DifferentialChangeType::FILE_IMAGE:
|
||||
$message = pht('This is an image.');
|
||||
$messages[] = pht('This is an image.');
|
||||
break;
|
||||
case DifferentialChangeType::FILE_DIRECTORY:
|
||||
$message = pht('This is a directory.');
|
||||
$messages[] = pht('This is a directory.');
|
||||
break;
|
||||
case DifferentialChangeType::FILE_BINARY:
|
||||
$message = pht('This is a binary file.');
|
||||
$messages[] = pht('This is a binary file.');
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SYMLINK:
|
||||
$message = pht('This is a symlink.');
|
||||
$messages[] = pht('This is a symlink.');
|
||||
break;
|
||||
case DifferentialChangeType::FILE_SUBMODULE:
|
||||
$message = pht('This is a submodule.');
|
||||
$messages[] = pht('This is a submodule.');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$encoding = $this->getOriginalCharacterEncoding();
|
||||
if ($encoding != 'utf8' && ($file == DifferentialChangeType::FILE_TEXT)) {
|
||||
if ($encoding) {
|
||||
$messages[] = pht(
|
||||
'This file was converted from %s for display.',
|
||||
phutil_tag('strong', array(), $encoding));
|
||||
} else {
|
||||
$messages[] = pht(
|
||||
'This file uses an unknown character encoding.');
|
||||
}
|
||||
}
|
||||
|
||||
return phutil_tag_div('differential-meta-notice', $message);
|
||||
if (!$messages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($messages as $key => $message) {
|
||||
$messages[$key] = phutil_tag('li', array(), $message);
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'ul',
|
||||
array(
|
||||
'class' => 'differential-meta-notice',
|
||||
),
|
||||
$messages);
|
||||
}
|
||||
|
||||
protected function renderPropertyChangeHeader() {
|
||||
|
|
|
@ -28,6 +28,16 @@ abstract class DifferentialChangesetRenderer {
|
|||
private $gaps;
|
||||
private $mask;
|
||||
private $depths;
|
||||
private $originalCharacterEncoding;
|
||||
|
||||
public function setOriginalCharacterEncoding($original_character_encoding) {
|
||||
$this->originalCharacterEncoding = $original_character_encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOriginalCharacterEncoding() {
|
||||
return $this->originalCharacterEncoding;
|
||||
}
|
||||
|
||||
public function setDepths($depths) {
|
||||
$this->depths = $depths;
|
||||
|
|
|
@ -8,4 +8,13 @@ final class DifferentialHunkLegacy extends DifferentialHunk {
|
|||
return 'differential_hunk';
|
||||
}
|
||||
|
||||
public function getDataEncoding() {
|
||||
return 'utf8';
|
||||
}
|
||||
|
||||
public function forceEncoding($encoding) {
|
||||
// Not supported, these are always utf8.
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ final class DifferentialHunkModern extends DifferentialHunk {
|
|||
protected $data;
|
||||
|
||||
private $rawData;
|
||||
private $forcedEncoding;
|
||||
|
||||
public function getTableName() {
|
||||
return 'differential_hunk_modern';
|
||||
|
@ -41,7 +42,12 @@ final class DifferentialHunkModern extends DifferentialHunk {
|
|||
public function getChanges() {
|
||||
return $this->getUTF8StringFromStorage(
|
||||
$this->getRawData(),
|
||||
$this->getDataEncoding());
|
||||
nonempty($this->forcedEncoding, $this->getDataEncoding()));
|
||||
}
|
||||
|
||||
public function forceEncoding($encoding) {
|
||||
$this->forcedEncoding = $encoding;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function save() {
|
||||
|
|
|
@ -127,6 +127,11 @@ final class DifferentialChangesetListView extends AphrontView {
|
|||
'Show Raw File (Left)' => pht('Show Raw File (Left)'),
|
||||
'Show Raw File (Right)' => pht('Show Raw File (Right)'),
|
||||
'Configure Editor' => pht('Configure Editor'),
|
||||
'Load Changes' => pht('Load Changes'),
|
||||
'View Side-by-Side' => pht('View Side-by-Side'),
|
||||
'View Unified' => pht('View Unified (Barely Works!)'),
|
||||
'Change Text Encoding...' => pht('Change Text Encoding...'),
|
||||
'Highlight As...' => pht('Highlight As...'),
|
||||
),
|
||||
));
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ final class DiffusionDiffController extends DiffusionController {
|
|||
array(
|
||||
'action' => 'rendering-ref')));
|
||||
|
||||
$parser->setCharacterEncoding($request->getStr('encoding'));
|
||||
$parser->setHighlightAs($request->getStr('highlight'));
|
||||
|
||||
$coverage = $drequest->loadCoverage();
|
||||
if ($coverage) {
|
||||
$parser->setCoverage($coverage);
|
||||
|
|
|
@ -15,6 +15,10 @@ final class PhabricatorApplicationSystem extends PhabricatorApplication {
|
|||
'/status/' => 'PhabricatorStatusController',
|
||||
'/debug/' => 'PhabricatorDebugController',
|
||||
'/robots.txt' => 'PhabricatorRobotsController',
|
||||
'/services/' => array(
|
||||
'encoding/' => 'PhabricatorSystemSelectEncodingController',
|
||||
'highlight/' => 'PhabricatorSystemSelectHighlightController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSystemSelectEncodingController
|
||||
extends PhabricatorController {
|
||||
|
||||
public function shouldRequireLogin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
|
||||
if (!function_exists('mb_list_encodings')) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('No Encoding Support'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'This system does not have the "mbstring" extension installed, '.
|
||||
'so character encodings are not supported. Install "mbstring" to '.
|
||||
'enable support.'))
|
||||
->addCancelButton('/');
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$result = array('encoding' => $request->getStr('encoding'));
|
||||
return id(new AphrontAjaxResponse())->setContent($result);
|
||||
}
|
||||
|
||||
$encodings = mb_list_encodings();
|
||||
$encodings = array_fuse($encodings);
|
||||
asort($encodings);
|
||||
unset($encodings['pass']);
|
||||
unset($encodings['auto']);
|
||||
|
||||
$encodings = array(
|
||||
'' => pht('(Use Default)'),
|
||||
) + $encodings;
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($this->getRequest()->getUser())
|
||||
->appendRemarkupInstructions(pht('Choose a text encoding to use.'))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Encoding'))
|
||||
->setName('encoding')
|
||||
->setValue($request->getStr('encoding'))
|
||||
->setOptions($encodings));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Select Character Encoding'))
|
||||
->appendChild($form->buildLayoutView())
|
||||
->addSubmitButton(pht('Choose Encoding'))
|
||||
->addCancelButton('/');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSystemSelectHighlightController
|
||||
extends PhabricatorController {
|
||||
|
||||
public function shouldRequireLogin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$result = array('highlight' => $request->getStr('highlight'));
|
||||
return id(new AphrontAjaxResponse())->setContent($result);
|
||||
}
|
||||
|
||||
$languages = array(
|
||||
'' => pht('(Use Default)'),
|
||||
) + PhabricatorEnv::getEnvConfig('pygments.dropdown-choices');
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($this->getRequest()->getUser())
|
||||
->appendRemarkupInstructions(pht('Choose a syntax highlighting to use.'))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Highlighting'))
|
||||
->setName('highlight')
|
||||
->setValue($request->getStr('highlight'))
|
||||
->setOptions($languages));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Select Syntax Highlighting'))
|
||||
->appendChild($form->buildLayoutView())
|
||||
->addSubmitButton(pht('Choose Highlighting'))
|
||||
->addCancelButton('/');
|
||||
}
|
||||
}
|
|
@ -183,6 +183,29 @@ abstract class PhabricatorLiskDAO extends LiskDAO {
|
|||
if ($encoding == 'utf8') {
|
||||
return $string;
|
||||
}
|
||||
|
||||
if (function_exists('mb_detect_encoding')) {
|
||||
if (strlen($encoding)) {
|
||||
$try_encodings = array(
|
||||
$encoding,
|
||||
);
|
||||
} else {
|
||||
// TODO: This is pretty much a guess, and probably needs to be
|
||||
// configurable in the long run.
|
||||
$try_encodings = array(
|
||||
'JIS',
|
||||
'EUC-JP',
|
||||
'SJIS',
|
||||
'ISO-8859-1',
|
||||
);
|
||||
}
|
||||
|
||||
$guess = mb_detect_encoding($string, $try_encodings);
|
||||
if ($guess) {
|
||||
return mb_convert_encoding($string, 'UTF-8', $guess);
|
||||
}
|
||||
}
|
||||
|
||||
return phutil_utf8ize($string);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ JX.install('ChangesetViewManager', {
|
|||
this._whitespace = data.whitespace;
|
||||
this._renderer = data.renderer;
|
||||
this._highlight = data.highlight;
|
||||
this._encoding = data.encoding;
|
||||
},
|
||||
|
||||
members: {
|
||||
|
@ -35,6 +36,7 @@ JX.install('ChangesetViewManager', {
|
|||
_whitespace: null,
|
||||
_renderer: null,
|
||||
_highlight: null,
|
||||
_encoding: null,
|
||||
|
||||
|
||||
/**
|
||||
|
@ -120,8 +122,10 @@ JX.install('ChangesetViewManager', {
|
|||
|
||||
var params = {
|
||||
ref: this._ref,
|
||||
whitespace: this._whitespace,
|
||||
renderer: this._getRenderer()
|
||||
whitespace: this._whitespace || '',
|
||||
renderer: this.getRenderer() || '',
|
||||
highlight: this._highlight || '',
|
||||
encoding: this._encoding || ''
|
||||
};
|
||||
|
||||
var workflow = new JX.Workflow(this._renderURI, params)
|
||||
|
@ -160,8 +164,16 @@ JX.install('ChangesetViewManager', {
|
|||
return JX.Router.getInstance().getRoutableByKey(this._getRoutableKey());
|
||||
},
|
||||
|
||||
setRenderer: function(renderer) {
|
||||
this._renderer = renderer;
|
||||
return this;
|
||||
},
|
||||
|
||||
getRenderer: function() {
|
||||
if (this._renderer !== null) {
|
||||
return this._renderer;
|
||||
}
|
||||
|
||||
_getRenderer: function() {
|
||||
// TODO: This is a big pile of TODOs.
|
||||
|
||||
// NOTE: If you load the page at one device resolution and then resize to
|
||||
|
@ -176,6 +188,23 @@ JX.install('ChangesetViewManager', {
|
|||
return renderer;
|
||||
},
|
||||
|
||||
setEncoding: function(encoding) {
|
||||
this._encoding = encoding;
|
||||
return this;
|
||||
},
|
||||
|
||||
getEncoding: function() {
|
||||
return this._encoding;
|
||||
},
|
||||
|
||||
setHighlight: function(highlight) {
|
||||
this._highlight = highlight;
|
||||
return this;
|
||||
},
|
||||
|
||||
getHighlight: function() {
|
||||
return this._highlight;
|
||||
},
|
||||
|
||||
_getNodeData: function() {
|
||||
return JX.Stratcom.getData(this._node);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* javelin-dom
|
||||
* javelin-util
|
||||
* javelin-stratcom
|
||||
* javelin-workflow
|
||||
* phuix-dropdown-menu
|
||||
* phuix-action-list-view
|
||||
* phuix-action-view
|
||||
|
@ -48,13 +49,18 @@ JX.behavior('differential-dropdown-menus', function(config) {
|
|||
var buildmenu = function(e) {
|
||||
var button = e.getNode('differential-view-options');
|
||||
var data = JX.Stratcom.getData(button);
|
||||
|
||||
if (data.menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.prevent();
|
||||
|
||||
var changeset = JX.DOM.findAbove(
|
||||
button,
|
||||
'div',
|
||||
'differential-changeset');
|
||||
|
||||
var view = JX.ChangesetViewManager.getForNode(changeset);
|
||||
var menu = new JX.PHUIXDropdownMenu(button);
|
||||
var list = new JX.PHUIXActionListView();
|
||||
|
||||
|
@ -103,12 +109,15 @@ JX.behavior('differential-dropdown-menus', function(config) {
|
|||
|
||||
var up_item = new JX.PHUIXActionView()
|
||||
.setHandler(function(e) {
|
||||
var changeset = JX.DOM.findAbove(
|
||||
button,
|
||||
'div',
|
||||
'differential-changeset');
|
||||
|
||||
var view = JX.ChangesetViewManager.getForNode(changeset);
|
||||
if (view.isLoaded()) {
|
||||
var renderer = view.getRenderer();
|
||||
if (renderer == '1up') {
|
||||
renderer = '2up';
|
||||
} else {
|
||||
renderer = '1up';
|
||||
}
|
||||
view.setRenderer(renderer);
|
||||
}
|
||||
view.reload();
|
||||
|
||||
e.prevent();
|
||||
|
@ -116,22 +125,54 @@ JX.behavior('differential-dropdown-menus', function(config) {
|
|||
});
|
||||
list.addItem(up_item);
|
||||
|
||||
var encoding_item = new JX.PHUIXActionView()
|
||||
.setIcon('fa-font')
|
||||
.setName(pht('Change Text Encoding...'))
|
||||
.setHandler(function(e) {
|
||||
var params = {
|
||||
encoding: view.getEncoding()
|
||||
};
|
||||
|
||||
new JX.Workflow('/services/encoding/', params)
|
||||
.setHandler(function(r) {
|
||||
view.setEncoding(r.encoding);
|
||||
view.reload();
|
||||
})
|
||||
.start();
|
||||
|
||||
e.prevent();
|
||||
menu.close();
|
||||
});
|
||||
list.addItem(encoding_item);
|
||||
|
||||
var highlight_item = new JX.PHUIXActionView()
|
||||
.setIcon('fa-sun-o')
|
||||
.setName(pht('Highlight As...'))
|
||||
.setHandler(function(e) {
|
||||
var params = {
|
||||
highlight: view.getHighlight()
|
||||
};
|
||||
|
||||
new JX.Workflow('/services/highlight/', params)
|
||||
.setHandler(function(r) {
|
||||
view.setHighlight(r.highlight);
|
||||
view.reload();
|
||||
})
|
||||
.start();
|
||||
|
||||
e.prevent();
|
||||
menu.close();
|
||||
});
|
||||
list.addItem(highlight_item);
|
||||
|
||||
add_link('fa-arrow-left', pht('Show Raw File (Left)'), data.leftURI);
|
||||
add_link('fa-arrow-right', pht('Show Raw File (Right)'), data.rightURI);
|
||||
add_link('fa-pencil', pht('Open in Editor'), data.editor, true);
|
||||
add_link('fa-wrench', pht('Configure Editor'), data.editorConfigure);
|
||||
|
||||
|
||||
menu.setContent(list.getNode());
|
||||
|
||||
menu.listen('open', function() {
|
||||
var changeset = JX.DOM.findAbove(
|
||||
button,
|
||||
'div',
|
||||
'differential-changeset');
|
||||
|
||||
var view = JX.ChangesetViewManager.getForNode(changeset);
|
||||
|
||||
// When the user opens the menu, check if there are any "Show More"
|
||||
// links in the changeset body. If there aren't, disable the "Show
|
||||
// Entire File" menu item since it won't change anything.
|
||||
|
@ -155,16 +196,23 @@ JX.behavior('differential-dropdown-menus', function(config) {
|
|||
.setHandler(function(e) { e.prevent(); });
|
||||
}
|
||||
|
||||
// TODO: This is temporary and just makes testing easier. It will do
|
||||
// some mojo soon.
|
||||
encoding_item.setDisabled(!view.isLoaded());
|
||||
highlight_item.setDisabled(!view.isLoaded());
|
||||
|
||||
if (view.isLoaded()) {
|
||||
if (view.getRenderer() == '2up') {
|
||||
up_item
|
||||
.setIcon('fa-refresh')
|
||||
.setName('Reload');
|
||||
.setIcon('fa-list-alt')
|
||||
.setName(pht('View Unified'));
|
||||
} else {
|
||||
up_item
|
||||
.setIcon('fa-files-o')
|
||||
.setName(pht('View Side-by-Side'));
|
||||
}
|
||||
} else {
|
||||
up_item
|
||||
.setIcon('fa-refresh')
|
||||
.setName('Load');
|
||||
.setName(pht('Load Changes'));
|
||||
}
|
||||
|
||||
visible_item
|
||||
|
@ -198,6 +246,7 @@ JX.behavior('differential-dropdown-menus', function(config) {
|
|||
}
|
||||
|
||||
});
|
||||
|
||||
data.menu = menu;
|
||||
menu.open();
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue