diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 30cdffe911..413fa3b7ed 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -1140,7 +1140,7 @@ celerity_register_resource_map(array( ), 'diffusion-source-css' => array( - 'uri' => '/res/e76bcd50/rsrc/css/application/diffusion/diffusion-source.css', + 'uri' => '/res/162c8794/rsrc/css/application/diffusion/diffusion-source.css', 'type' => 'css', 'requires' => array( @@ -1667,19 +1667,6 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/diffusion/behavior-jump-to.js', ), - 'javelin-behavior-diffusion-line-linker' => - array( - 'uri' => '/res/12866f13/rsrc/js/application/diffusion/behavior-line-linker.js', - 'type' => 'js', - 'requires' => - array( - 0 => 'javelin-behavior', - 1 => 'javelin-stratcom', - 2 => 'javelin-dom', - 3 => 'javelin-history', - ), - 'disk' => '/rsrc/js/application/diffusion/behavior-line-linker.js', - ), 'javelin-behavior-diffusion-pull-lastmodified' => array( 'uri' => '/res/29fe2790/rsrc/js/application/diffusion/behavior-pull-lastmodified.js', @@ -2067,6 +2054,19 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/core/behavior-keyboard-shortcuts.js', ), + 'javelin-behavior-phabricator-line-linker' => + array( + 'uri' => '/res/1cefdb6a/rsrc/js/core/behavior-line-linker.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-stratcom', + 2 => 'javelin-dom', + 3 => 'javelin-history', + ), + 'disk' => '/rsrc/js/core/behavior-line-linker.js', + ), 'javelin-behavior-phabricator-nav' => array( 'uri' => '/res/afabcf16/rsrc/js/core/behavior-phabricator-nav.js', @@ -2249,7 +2249,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-pholio-mock-view' => array( - 'uri' => '/res/3ff82e28/rsrc/js/application/pholio/behavior-pholio-mock-view.js', + 'uri' => '/res/f9588dcf/rsrc/js/application/pholio/behavior-pholio-mock-view.js', 'type' => 'js', 'requires' => array( @@ -3498,7 +3498,7 @@ celerity_register_resource_map(array( ), 'phabricator-source-code-view-css' => array( - 'uri' => '/res/979d5280/rsrc/css/layout/phabricator-source-code-view.css', + 'uri' => '/res/70bcbea4/rsrc/css/layout/phabricator-source-code-view.css', 'type' => 'css', 'requires' => array( diff --git a/src/applications/diffusion/controller/DiffusionBrowseFileController.php b/src/applications/diffusion/controller/DiffusionBrowseFileController.php index bcdc755074..3a09039a88 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseFileController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseFileController.php @@ -241,7 +241,7 @@ final class DiffusionBrowseFileController extends DiffusionController { 'table', array( 'class' => "diffusion-source remarkup-code PhabricatorMonospaced", - 'sigil' => 'diffusion-source', + 'sigil' => 'phabricator-source', ), $rows); @@ -728,12 +728,12 @@ final class DiffusionBrowseFileController extends DiffusionController { 'th', array( 'class' => 'diffusion-line-link', - 'sigil' => 'diffusion-line-link', + 'sigil' => 'phabricator-source-line', 'style' => $style, ), $line_link); - Javelin::initBehavior('diffusion-line-linker'); + Javelin::initBehavior('phabricator-line-linker'); if ($line['target']) { Javelin::initBehavior( @@ -768,7 +768,9 @@ final class DiffusionBrowseFileController extends DiffusionController { $rows[] = phutil_tag( 'tr', array( - 'class' => ($line['highlighted'] ? 'highlighted' : null), + 'class' => ($line['highlighted'] ? + 'phabricator-source-highlight' : + null), ), $blame); diff --git a/src/applications/paste/application/PhabricatorApplicationPaste.php b/src/applications/paste/application/PhabricatorApplicationPaste.php index d1181566bc..68561ecf2b 100644 --- a/src/applications/paste/application/PhabricatorApplicationPaste.php +++ b/src/applications/paste/application/PhabricatorApplicationPaste.php @@ -33,12 +33,13 @@ final class PhabricatorApplicationPaste extends PhabricatorApplication { public function getRoutes() { return array( - '/P(?P[1-9]\d*)' => 'PhabricatorPasteViewController', + '/P(?P[1-9]\d*)(?:\$(?P\d+(?:-\d+)?))?' + => 'PhabricatorPasteViewController', '/paste/' => array( '(query/(?P[^/]+)/)?' => 'PhabricatorPasteListController', 'create/' => 'PhabricatorPasteEditController', 'edit/(?P[1-9]\d*)/' => 'PhabricatorPasteEditController', - 'comment/(?P[1-9]\d*)/' => 'PhabricatorPasteCommentController', + 'comment/(?P[1-9]\d*)/' => 'PhabricatorPasteCommentController', ), ); } diff --git a/src/applications/paste/controller/PhabricatorPasteController.php b/src/applications/paste/controller/PhabricatorPasteController.php index 034d77196a..23d40b0867 100644 --- a/src/applications/paste/controller/PhabricatorPasteController.php +++ b/src/applications/paste/controller/PhabricatorPasteController.php @@ -42,13 +42,16 @@ abstract class PhabricatorPasteController extends PhabricatorController { public function buildSourceCodeView( PhabricatorPaste $paste, - $max_lines = null) { + $max_lines = null, + $highlights = array()) { $lines = phutil_split_lines($paste->getContent()); return id(new PhabricatorSourceCodeView()) ->setLimit($max_lines) - ->setLines($lines); + ->setLines($lines) + ->setHighlights($highlights) + ->setURI(new PhutilURI($paste->getURI())); } } diff --git a/src/applications/paste/controller/PhabricatorPasteViewController.php b/src/applications/paste/controller/PhabricatorPasteViewController.php index 99d18492f9..cb8d9cfdf8 100644 --- a/src/applications/paste/controller/PhabricatorPasteViewController.php +++ b/src/applications/paste/controller/PhabricatorPasteViewController.php @@ -6,6 +6,7 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController { private $id; + private $highlightMap; public function shouldAllowPublic() { return true; @@ -13,6 +14,21 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController { public function willProcessRequest(array $data) { $this->id = $data['id']; + $raw_lines = idx($data, 'lines'); + $map = array(); + if ($raw_lines) { + $lines = explode('-', $raw_lines); + $first = idx($lines, 0, 0); + $last = idx($lines, 1); + if ($last) { + $min = min($first, $last); + $max = max($first, $last); + $map = array_fuse(range($min, $max)); + } else { + $map[$first] = $first; + } + } + $this->highlightMap = $map; } public function processRequest() { @@ -52,7 +68,10 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController { $header = $this->buildHeaderView($paste); $actions = $this->buildActionView($user, $paste, $file); $properties = $this->buildPropertyView($paste, $fork_phids); - $source_code = $this->buildSourceCodeView($paste); + $source_code = $this->buildSourceCodeView( + $paste, + null, + $this->highlightMap); $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()) ->setActionList($actions) diff --git a/src/view/layout/PhabricatorSourceCodeView.php b/src/view/layout/PhabricatorSourceCodeView.php index 12d051771c..717de7f60c 100644 --- a/src/view/layout/PhabricatorSourceCodeView.php +++ b/src/view/layout/PhabricatorSourceCodeView.php @@ -4,6 +4,7 @@ final class PhabricatorSourceCodeView extends AphrontView { private $lines; private $limit; + private $uri; private $highlights = array(); public function setLimit($limit) { @@ -16,8 +17,13 @@ final class PhabricatorSourceCodeView extends AphrontView { return $this; } - public function setHighlights(array $highlights) { - $this->highlights = array_fuse($highlights); + public function setURI(PhutilURI $uri) { + $this->uri = $uri; + return $this; + } + + public function setHighlights(array $array) { + $this->highlights = array_fuse($array); return $this; } @@ -26,10 +32,12 @@ final class PhabricatorSourceCodeView extends AphrontView { require_celerity_resource('syntax-highlighting-css'); Javelin::initBehavior('phabricator-oncopy', array()); + Javelin::initBehavior('phabricator-line-linker'); $line_number = 1; $rows = array(); + foreach ($this->lines as $line) { $hit_limit = $this->limit && ($line_number == $this->limit) && @@ -53,16 +61,33 @@ final class PhabricatorSourceCodeView extends AphrontView { $row_attributes['class'] = 'phabricator-source-highlight'; } - // TODO: Provide nice links. + $line_uri = $this->uri . "$" . $line_number; + $line_href = (string) new PhutilURI($line_uri); + + $tag_number = javelin_tag( + 'a', + array( + 'href' => $line_href + ), + $line_number); $rows[] = phutil_tag( 'tr', $row_attributes, - hsprintf( - '%s'. - '%s', - $content_number, - $content_line)); + array( + javelin_tag( + 'th', + array( + 'class' => 'phabricator-source-line', + 'sigil' => 'phabricator-source-line' + ), + $tag_number), + phutil_tag( + 'td', + array( + 'class' => 'phabricator-source-code' + ), + $content_line))); if ($hit_limit) { break; @@ -81,10 +106,11 @@ final class PhabricatorSourceCodeView extends AphrontView { array( 'class' => 'phabricator-source-code-container', ), - phutil_tag( + javelin_tag( 'table', array( 'class' => implode(' ', $classes), + 'sigil' => 'phabricator-source' ), phutil_implode_html('', $rows))); } diff --git a/webroot/rsrc/css/application/diffusion/diffusion-source.css b/webroot/rsrc/css/application/diffusion/diffusion-source.css index ce8def486e..21d5d61b45 100644 --- a/webroot/rsrc/css/application/diffusion/diffusion-source.css +++ b/webroot/rsrc/css/application/diffusion/diffusion-source.css @@ -9,7 +9,7 @@ font-size: 10px; } -.diffusion-source tr.highlighted { +.diffusion-source tr.phabricator-source-highlight { background: #ffff00; } diff --git a/webroot/rsrc/css/layout/phabricator-source-code-view.css b/webroot/rsrc/css/layout/phabricator-source-code-view.css index dcb8f2faef..a3110b66fd 100644 --- a/webroot/rsrc/css/layout/phabricator-source-code-view.css +++ b/webroot/rsrc/css/layout/phabricator-source-code-view.css @@ -10,6 +10,7 @@ .phabricator-source-code { white-space: pre-wrap; padding: 2px 8px 1px; + width: 100%; } .phabricator-source-line { diff --git a/webroot/rsrc/js/application/diffusion/behavior-line-linker.js b/webroot/rsrc/js/core/behavior-line-linker.js similarity index 70% rename from webroot/rsrc/js/application/diffusion/behavior-line-linker.js rename to webroot/rsrc/js/core/behavior-line-linker.js index b08f3b0271..9175b725e8 100644 --- a/webroot/rsrc/js/application/diffusion/behavior-line-linker.js +++ b/webroot/rsrc/js/core/behavior-line-linker.js @@ -1,44 +1,43 @@ /** - * @provides javelin-behavior-diffusion-line-linker + * @provides javelin-behavior-phabricator-line-linker * @requires javelin-behavior * javelin-stratcom * javelin-dom * javelin-history */ -JX.behavior('diffusion-line-linker', function() { - +JX.behavior('phabricator-line-linker', function() { var origin = null; var target = null; var root = null; function getRowNumber(tr) { - var th = JX.DOM.find(tr, 'th', 'diffusion-line-link'); + var th = JX.DOM.find(tr, 'th', 'phabricator-source-line'); return +(th.textContent || th.innerText); } JX.Stratcom.listen( 'mousedown', - 'diffusion-line-link', + 'phabricator-source-line', function(e) { - if (e.isRightButton()) { + if (!e.isNormalMouseEvent()) { return; } origin = e.getNode('tag:tr'); target = origin; - root = e.getNode('diffusion-source'); + root = e.getNode('phabricator-source'); e.kill(); }); JX.Stratcom.listen( 'click', - 'diffusion-line-link', + 'phabricator-source-line', function(e) { e.kill(); }); var highlight = function(e) { - if (!origin || e.getNode('diffusion-source') !== root) { + if (!origin || e.getNode('phabricator-source') !== root) { return; } target = e.getNode('tag:tr'); @@ -51,14 +50,14 @@ JX.behavior('diffusion-line-linker', function() { highlighting = true; source = trs[i]; } - JX.DOM.alterClass(trs[i], 'highlighted', highlighting); + JX.DOM.alterClass(trs[i], 'phabricator-source-highlight', highlighting); if (trs[i] === (source === origin ? target : origin)) { highlighting = false; } } }; - JX.Stratcom.listen('mouseover', 'diffusion-source', highlight); + JX.Stratcom.listen('mouseover', 'phabricator-source', highlight); JX.Stratcom.listen( 'mouseup', @@ -73,10 +72,9 @@ JX.behavior('diffusion-line-linker', function() { var o = getRowNumber(origin); var t = getRowNumber(target); var lines = (o == t ? o : Math.min(o, t) + '-' + Math.max(o, t)); - var th = JX.DOM.find(origin, 'th', 'diffusion-line-link'); + var th = JX.DOM.find(origin, 'th', 'phabricator-source-line'); var uri = JX.DOM.find(th, 'a').href; uri = uri.replace(/(.*\$)\d+/, '$1' + lines); - origin = null; target = null; e.kill();