diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -119,7 +119,7 @@ 'rsrc/css/font/font-lato.css' => 'c7ccd872', 'rsrc/css/font/phui-font-icon-base.css' => '870a7360', 'rsrc/css/layout/phabricator-filetree-view.css' => 'b912ad97', - 'rsrc/css/layout/phabricator-source-code-view.css' => '09368218', + 'rsrc/css/layout/phabricator-source-code-view.css' => 'fdbefca0', 'rsrc/css/phui/button/phui-button-bar.css' => 'f1ff5494', 'rsrc/css/phui/button/phui-button-simple.css' => '8e1baf68', 'rsrc/css/phui/button/phui-button.css' => '1863cc6e', @@ -388,7 +388,7 @@ 'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc', 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '1db13e70', 'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef', - 'rsrc/js/application/files/behavior-document-engine.js' => '0333c0b6', + 'rsrc/js/application/files/behavior-document-engine.js' => 'ee0deff8', 'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab', 'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888', 'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '191b4909', @@ -600,7 +600,7 @@ 'javelin-behavior-diffusion-commit-graph' => '75b83cbb', 'javelin-behavior-diffusion-locate-file' => '6d3e1947', 'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc', - 'javelin-behavior-document-engine' => '0333c0b6', + 'javelin-behavior-document-engine' => 'ee0deff8', 'javelin-behavior-doorkeeper-tag' => '1db13e70', 'javelin-behavior-drydock-live-operation-status' => '901935ef', 'javelin-behavior-durable-column' => '2ae077e1', @@ -776,7 +776,7 @@ 'phabricator-search-results-css' => '505dd8cf', 'phabricator-shaped-request' => '7cbe244b', 'phabricator-slowvote-css' => 'a94b7230', - 'phabricator-source-code-view-css' => '09368218', + 'phabricator-source-code-view-css' => 'fdbefca0', 'phabricator-standard-page-view' => '34ee718b', 'phabricator-textareautils' => '320810c8', 'phabricator-title' => '485aaa6c', @@ -905,11 +905,6 @@ 'javelin-behavior', 'javelin-uri', ), - '0333c0b6' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - ), '040fce04' => array( 'javelin-behavior', 'javelin-request', @@ -2110,6 +2105,11 @@ 'javelin-behavior', 'javelin-uri', ), + 'ee0deff8' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + ), 'efe49472' => array( 'javelin-install', 'javelin-util', diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php --- a/src/applications/diffusion/controller/DiffusionBrowseController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseController.php @@ -4,7 +4,6 @@ private $lintCommit; private $lintMessages; - private $coverage; private $corpusButtons = array(); public function shouldAllowPublic() { @@ -182,7 +181,6 @@ $corpus = $this->buildGitLFSCorpus($lfs_ref); } else { - $this->coverage = $drequest->loadCoverage(); $show_editor = true; $ref = id(new PhabricatorDocumentRef()) diff --git a/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php b/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php --- a/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php +++ b/src/applications/diffusion/document/DiffusionDocumentRenderingEngine.php @@ -81,6 +81,11 @@ $ref ->setSymbolMetadata($this->getSymbolMetadata()) ->setBlameURI($blame_uri); + + $coverage = $drequest->loadCoverage(); + if (strlen($coverage)) { + $ref->addCoverage($coverage); + } } private function getSymbolMetadata() { diff --git a/src/applications/files/document/PhabricatorDocumentRef.php b/src/applications/files/document/PhabricatorDocumentRef.php --- a/src/applications/files/document/PhabricatorDocumentRef.php +++ b/src/applications/files/document/PhabricatorDocumentRef.php @@ -10,6 +10,7 @@ private $snippet; private $symbolMetadata = array(); private $blameURI; + private $coverage = array(); public function setFile(PhabricatorFile $file) { $this->file = $file; @@ -151,4 +152,15 @@ return $this->blameURI; } + public function addCoverage($coverage) { + $this->coverage[] = array( + 'data' => $coverage, + ); + return $this; + } + + public function getCoverage() { + return $this->coverage; + } + } diff --git a/src/applications/files/document/PhabricatorSourceDocumentEngine.php b/src/applications/files/document/PhabricatorSourceDocumentEngine.php --- a/src/applications/files/document/PhabricatorSourceDocumentEngine.php +++ b/src/applications/files/document/PhabricatorSourceDocumentEngine.php @@ -57,6 +57,10 @@ $options['blame'] = $blame; } + if ($ref->getCoverage()) { + $options['coverage'] = $ref->getCoverage(); + } + return array( $messages, $this->newTextDocumentContent($ref, $content, $options), diff --git a/src/applications/files/document/PhabricatorTextDocumentEngine.php b/src/applications/files/document/PhabricatorTextDocumentEngine.php --- a/src/applications/files/document/PhabricatorTextDocumentEngine.php +++ b/src/applications/files/document/PhabricatorTextDocumentEngine.php @@ -22,6 +22,7 @@ $options, array( 'blame' => 'optional wild', + 'coverage' => 'optional list', )); if (is_array($content)) { @@ -40,6 +41,11 @@ $view->setBlameMap($blame); } + $coverage = idx($options, 'coverage'); + if ($coverage !== null) { + $view->setCoverage($coverage); + } + $message = null; if ($this->encodingMessage !== null) { $message = $this->newMessage($this->encodingMessage); diff --git a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php --- a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php +++ b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php @@ -145,6 +145,17 @@ 'uri' => $ref->getBlameURI(), 'value' => null, ), + 'coverage' => array( + 'labels' => array( + // TODO: Modularize this properly, see T13125. + array( + 'C' => pht('Covered'), + 'U' => pht('Not Covered'), + 'N' => pht('Not Executable'), + 'X' => pht('Not Reachable'), + ), + ), + ), ); $view_button = id(new PHUIButtonView()) diff --git a/src/view/layout/PhabricatorSourceCodeView.php b/src/view/layout/PhabricatorSourceCodeView.php --- a/src/view/layout/PhabricatorSourceCodeView.php +++ b/src/view/layout/PhabricatorSourceCodeView.php @@ -10,6 +10,7 @@ private $truncatedFirstLines = false; private $symbolMetadata; private $blameMap; + private $coverage = array(); public function setLines(array $lines) { $this->lines = $lines; @@ -59,6 +60,15 @@ return $this->blameMap; } + public function setCoverage(array $coverage) { + $this->coverage = $coverage; + return $this; + } + + public function getCoverage() { + return $this->coverage; + } + public function render() { $blame_map = $this->getBlameMap(); $has_blame = ($blame_map !== null); @@ -97,6 +107,19 @@ $base_uri = (string)$this->uri; $wrote_anchor = false; + + $coverage = $this->getCoverage(); + $coverage_count = count($coverage); + $coverage_data = ipull($coverage, 'data'); + + // TODO: Modularize this properly, see T13125. + $coverage_map = array( + 'C' => 'background: #66bbff;', + 'U' => 'background: #dd8866;', + 'N' => 'background: #ddeeff;', + 'X' => 'background: #aa00aa;', + ); + foreach ($lines as $line) { $row_attributes = array(); if (isset($this->highlights[$line_number])) { @@ -157,6 +180,25 @@ $blame_cells = null; } + $coverage_cells = array(); + foreach ($coverage as $coverage_idx => $coverage_spec) { + if (isset($coverage_spec['data'][$line_number - 1])) { + $coverage_char = $coverage_spec['data'][$line_number - 1]; + } else { + $coverage_char = null; + } + + $coverage_style = idx($coverage_map, $coverage_char, null); + + $coverage_cells[] = phutil_tag( + 'th', + array( + 'class' => 'phabricator-source-coverage', + 'style' => $coverage_style, + 'data-coverage' => $coverage_idx.'/'.$coverage_char, + )); + } + $rows[] = phutil_tag( 'tr', $row_attributes, @@ -174,7 +216,8 @@ 'class' => 'phabricator-source-code', ), $line), - )); + $coverage_cells, + )); $line_number++; } diff --git a/webroot/rsrc/css/layout/phabricator-source-code-view.css b/webroot/rsrc/css/layout/phabricator-source-code-view.css --- a/webroot/rsrc/css/layout/phabricator-source-code-view.css +++ b/webroot/rsrc/css/layout/phabricator-source-code-view.css @@ -6,7 +6,6 @@ overflow-x: auto; overflow-y: hidden; border: 1px solid {$paste.border}; - border-radius: 3px; } .phui-oi .phabricator-source-code-container { @@ -47,10 +46,12 @@ text-decoration: none; } +.phabricator-source-coverage-highlight .phabricator-source-code, .phabricator-source-highlight .phabricator-source-code { background: {$paste.highlight}; } +.phabricator-source-coverage-highlight .phabricator-source-line, .phabricator-source-highlight .phabricator-source-line { background: {$paste.border}; } @@ -96,7 +97,7 @@ .phabricator-source-blame-info a { color: {$darkbluetext}; - text-shadow: 1px 1px rgba(0, 0, 0, 0.111); + text-shadow: 1px 1px rgba(0, 0, 0, 0.05); } .phabricator-source-blame-skip a { @@ -123,3 +124,10 @@ background-size: 100% 100%; background-repeat: no-repeat; } + +th.phabricator-source-coverage { + padding: 0 8px; + border-left: 1px solid {$thinblueborder}; + background: {$lightgreybackground}; + cursor: w-resize; +} diff --git a/webroot/rsrc/js/application/files/behavior-document-engine.js b/webroot/rsrc/js/application/files/behavior-document-engine.js --- a/webroot/rsrc/js/application/files/behavior-document-engine.js +++ b/webroot/rsrc/js/application/files/behavior-document-engine.js @@ -322,7 +322,7 @@ var h_max = 0.44; var h = h_min + ((h_max - h_min) * epoch_value); - var s = 0.44; + var s = 0.25; var v_min = 0.92; var v_max = 1.00; @@ -357,6 +357,57 @@ return 'rgb(' + r + ', ' + g + ', ' + b + ')'; } + function onhovercoverage(data, e) { + if (e.getType() === 'mouseout') { + redraw_coverage(data, null); + return; + } + + var target = e.getNode('tag:th'); + var coverage = target.getAttribute('data-coverage'); + if (!coverage) { + return; + } + + redraw_coverage(data, target); + } + + var coverage_row = null; + function redraw_coverage(data, node) { + if (coverage_row) { + JX.DOM.alterClass( + coverage_row, + 'phabricator-source-coverage-highlight', + false); + coverage_row = null; + } + + if (!node) { + JX.Tooltip.hide(); + return; + } + + var coverage = node.getAttribute('data-coverage'); + coverage = coverage.split('/'); + + var idx = parseInt(coverage[0], 10); + var chr = coverage[1]; + + var map = data.coverage.labels[idx]; + if (map) { + var label = map[chr]; + if (label) { + JX.Tooltip.show(node, 300, 'W', label); + + coverage_row = JX.DOM.findAbove(node, 'tr'); + JX.DOM.alterClass( + coverage_row, + 'phabricator-source-coverage-highlight', + true); + } + } + } + if (!statics.initialized) { JX.Stratcom.listen('click', 'document-engine-view-dropdown', onmenu); statics.initialized = true; @@ -374,6 +425,12 @@ blame(data); break; } + + JX.DOM.listen( + JX.$(data.viewportID), + ['mouseover', 'mouseout'], + 'tag:th', + JX.bind(null, onhovercoverage, data)); } });