Index: resources/celerity/map.php =================================================================== --- resources/celerity/map.php +++ resources/celerity/map.php @@ -7,7 +7,7 @@ return array( 'names' => array( - 'core.pkg.css' => '13b791fd', + 'core.pkg.css' => '882662b1', 'core.pkg.js' => 'c7854cc5', 'darkconsole.pkg.js' => 'ca8671ce', 'differential.pkg.css' => '5a65a762', @@ -25,7 +25,7 @@ 'rsrc/css/aphront/dialog-view.css' => 'dd9db96c', 'rsrc/css/aphront/error-view.css' => '16cd9949', 'rsrc/css/aphront/lightbox-attachment.css' => '686f8885', - 'rsrc/css/aphront/list-filter-view.css' => '9f0c29ac', + 'rsrc/css/aphront/list-filter-view.css' => 'ef989c67', 'rsrc/css/aphront/multi-column.css' => '05bbd016', 'rsrc/css/aphront/notification.css' => '6901121e', 'rsrc/css/aphront/pager-view.css' => '2e3539af', @@ -130,7 +130,7 @@ 'rsrc/css/phui/phui-button.css' => '8106a67a', 'rsrc/css/phui/phui-document.css' => '143b2ac8', 'rsrc/css/phui/phui-feed-story.css' => '3a59c2cf', - 'rsrc/css/phui/phui-form-view.css' => '3179980c', + 'rsrc/css/phui/phui-form-view.css' => '0efd3326', 'rsrc/css/phui/phui-form.css' => 'b78ec020', 'rsrc/css/phui/phui-header-view.css' => '472a6003', 'rsrc/css/phui/phui-icon.css' => '29e83226', @@ -479,7 +479,7 @@ 'aphront-dark-console-css' => '6378ef3d', 'aphront-dialog-view-css' => 'dd9db96c', 'aphront-error-view-css' => '16cd9949', - 'aphront-list-filter-view-css' => '9f0c29ac', + 'aphront-list-filter-view-css' => 'ef989c67', 'aphront-multi-column-view-css' => '05bbd016', 'aphront-notes' => '6acadd3f', 'aphront-pager-view-css' => '2e3539af', @@ -739,7 +739,7 @@ 'phui-document-view-css' => '143b2ac8', 'phui-feed-story-css' => '3a59c2cf', 'phui-form-css' => 'b78ec020', - 'phui-form-view-css' => '3179980c', + 'phui-form-view-css' => '0efd3326', 'phui-header-view-css' => '472a6003', 'phui-icon-view-css' => '29e83226', 'phui-info-panel-css' => '27ea50a1', Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -48,6 +48,7 @@ 'AphrontFormSubmitControl' => 'view/form/control/AphrontFormSubmitControl.php', 'AphrontFormTextAreaControl' => 'view/form/control/AphrontFormTextAreaControl.php', 'AphrontFormTextControl' => 'view/form/control/AphrontFormTextControl.php', + 'AphrontFormTextWithSubmitControl' => 'view/form/control/AphrontFormTextWithSubmitControl.php', 'AphrontFormToggleButtonsControl' => 'view/form/control/AphrontFormToggleButtonsControl.php', 'AphrontFormTokenizerControl' => 'view/form/control/AphrontFormTokenizerControl.php', 'AphrontFormView' => 'view/form/AphrontFormView.php', @@ -2542,6 +2543,7 @@ 'AphrontFormSubmitControl' => 'AphrontFormControl', 'AphrontFormTextAreaControl' => 'AphrontFormControl', 'AphrontFormTextControl' => 'AphrontFormControl', + 'AphrontFormTextWithSubmitControl' => 'AphrontFormControl', 'AphrontFormToggleButtonsControl' => 'AphrontFormControl', 'AphrontFormTokenizerControl' => 'AphrontFormControl', 'AphrontFormView' => 'AphrontView', Index: src/applications/diffusion/controller/DiffusionBrowseController.php =================================================================== --- src/applications/diffusion/controller/DiffusionBrowseController.php +++ src/applications/diffusion/controller/DiffusionBrowseController.php @@ -8,38 +8,45 @@ protected function renderSearchForm($collapsed) { $drequest = $this->getDiffusionRequest(); + + $forms = array(); $form = id(new AphrontFormView()) ->setUser($this->getRequest()->getUser()) ->setMethod('GET'); switch ($drequest->getRepository()->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - $form->appendChild(pht('Search is not available in Subversion.')); + $forms[] = id(clone $form) + ->appendChild(pht('Search is not available in Subversion.')); break; - default: - $form + $forms[] = id(clone $form) ->appendChild( - id(new AphrontFormTextControl()) - ->setLabel(pht('Search Here')) - ->setName('grep') - ->setValue($this->getRequest()->getStr('grep')) - ->setCaption(pht('Enter a regular expression.'))) + id(new AphrontFormTextWithSubmitControl()) + ->setLabel(pht('File Name')) + ->setSubmitLabel(pht('Search File Names')) + ->setName('find') + ->setValue($this->getRequest()->getStr('find'))); + $forms[] = id(clone $form) ->appendChild( - id(new PHUIFormMultiSubmitControl()) - ->addButton('__ls__', pht('Search File Names')) - ->addButton('__grep__', pht('Search File Content'))); + id(new AphrontFormTextWithSubmitControl()) + ->setLabel(pht('Pattern')) + ->setSubmitLabel(pht('Grep File Content')) + ->setName('grep') + ->setValue($this->getRequest()->getStr('grep'))); break; } + $filter = new AphrontListFilterView(); - $filter->appendChild($form); + $filter->appendChild($forms); + if ($collapsed) { $filter->setCollapsed( pht('Show Search'), pht('Hide Search'), - pht('Search for file content in this directory.'), + pht('Search for file names or content in this directory.'), '#'); } Index: src/applications/diffusion/controller/DiffusionBrowseMainController.php =================================================================== --- src/applications/diffusion/controller/DiffusionBrowseMainController.php +++ src/applications/diffusion/controller/DiffusionBrowseMainController.php @@ -9,8 +9,9 @@ // Figure out if we're browsing a directory, a file, or a search result // list. Then delegate to the appropriate controller. - $search = $request->getStr('grep'); - if (strlen($search)) { + $grep = $request->getStr('grep'); + $find = $request->getStr('find'); + if (strlen($grep) || strlen($find)) { $controller = new DiffusionBrowseSearchController($request); } else { $results = DiffusionBrowseResultSet::newFromConduit( Index: src/applications/diffusion/controller/DiffusionBrowseSearchController.php =================================================================== --- src/applications/diffusion/controller/DiffusionBrowseSearchController.php +++ src/applications/diffusion/controller/DiffusionBrowseSearchController.php @@ -43,7 +43,6 @@ $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $results = array(); - $no_data = pht('No results found.'); $limit = 100; $page = $this->getRequest()->getInt('page', 0); @@ -52,29 +51,33 @@ $pager->setOffset($page); $pager->setURI($this->getRequest()->getRequestURI(), 'page'); + $search_mode = null; + try { - if ($this->getRequest()->getStr('__grep__')) { + if (strlen($this->getRequest()->getStr('grep'))) { + $search_mode = 'grep'; + $query_string = $this->getRequest()->getStr('grep'); $results = $this->callConduitWithDiffusionRequest( 'diffusion.searchquery', array( - 'grep' => $this->getRequest()->getStr('grep'), + 'grep' => $query_string, 'stableCommitName' => $drequest->getStableCommitName(), 'path' => $drequest->getPath(), 'limit' => $limit + 1, - 'offset' => $page)); + 'offset' => $page, + )); } else { // Filename search. - $results_raw = $this->callConduitWithDiffusionRequest( + $search_mode = 'find'; + $query_string = $this->getRequest()->getStr('find'); + $results = $this->callConduitWithDiffusionRequest( 'diffusion.querypaths', array( - 'pattern' => $this->getRequest()->getStr('grep'), + 'pattern' => $query_string, 'commit' => $drequest->getStableCommitName(), 'path' => $drequest->getPath(), 'limit' => $limit + 1, - 'offset' => $page)); - $results = []; - foreach ($results_raw as $result) { - $results[] = array($result, null, null); - } + 'offset' => $page, + )); } } catch (ConduitException $ex) { $err = $ex->getErrorDescription(); @@ -87,6 +90,34 @@ $results = $pager->sliceResults($results); + if ($search_mode == 'grep') { + $table = $this->renderGrepResults($results); + $header = pht( + 'File content matching "%s" under "%s"', + $query_string, + nonempty($drequest->getPath(), '/')); + } else { + $table = $this->renderFindResults($results); + $header = pht( + 'Paths matching "%s" under "%s"', + $query_string, + nonempty($drequest->getPath(), '/')); + } + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText($header) + ->appendChild($table); + + $pager_box = id(new PHUIBoxView()) + ->addMargin(PHUI::MARGIN_LARGE) + ->appendChild($pager); + + return array($box, $pager_box); + } + + private function renderGrepResults(array $results) { + $drequest = $this->getDiffusionRequest(); + require_celerity_resource('syntax-highlighting-css'); // NOTE: This can be wrong because we may find the string inside the @@ -139,12 +170,40 @@ ->setClassName('remarkup-code') ->setHeaders(array(pht('Path'), pht('Line'), pht('String'))) ->setColumnClasses(array('', 'n', 'wide')) - ->setNoDataString($no_data); + ->setNoDataString( + pht( + 'The pattern you searched for was not found in the content of any '. + 'files.')); - return id(new AphrontPanelView()) - ->setNoBackground(true) - ->appendChild($table) - ->appendChild($pager); + return $table; + } + + private function renderFindResults(array $results) { + $drequest = $this->getDiffusionRequest(); + + $rows = array(); + foreach ($results as $result) { + $href = $drequest->generateURI(array( + 'action' => 'browse', + 'path' => $result, + )); + + $readable = Filesystem::readablePath($result, $drequest->getPath()); + + $rows[] = array( + phutil_tag('a', array('href' => $href), $readable), + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders(array(pht('Path'))) + ->setColumnClasses(array('wide')) + ->setNoDataString( + pht( + 'The pattern you searched for did not match the names of any '. + 'files.')); + + return $table; } } Index: src/view/form/control/AphrontFormTextWithSubmitControl.php =================================================================== --- /dev/null +++ src/view/form/control/AphrontFormTextWithSubmitControl.php @@ -0,0 +1,57 @@ +submitLabel = $submit_label; + return $this; + } + + public function getSubmitLabel() { + return $this->submitLabel; + } + + protected function getCustomControlClass() { + return 'aphront-form-control-text-with-submit'; + } + + protected function renderInput() { + return phutil_tag( + 'div', + array( + 'class' => 'text-with-submit-control-outer-bounds', + ), + array( + phutil_tag( + 'div', + array( + 'class' => 'text-with-submit-control-text-bounds', + ), + javelin_tag( + 'input', + array( + 'type' => 'text', + 'class' => 'text-with-submit-control-text', + 'name' => $this->getName(), + 'value' => $this->getValue(), + 'disabled' => $this->getDisabled() ? 'disabled' : null, + 'id' => $this->getID(), + ))), + phutil_tag( + 'div', + array( + 'class' => 'text-with-submit-control-submit-bounds', + ), + javelin_tag( + 'input', + array( + 'type' => 'submit', + 'class' => 'text-with-submit-control-submit grey', + 'value' => coalesce($this->getSubmitLabel(), pht('Submit')) + ))), + )); + } + +} Index: webroot/rsrc/css/aphront/list-filter-view.css =================================================================== --- webroot/rsrc/css/aphront/list-filter-view.css +++ webroot/rsrc/css/aphront/list-filter-view.css @@ -24,6 +24,14 @@ padding: 12px 0 6px; } +/* When a list filter view contains two consecuitive forms, lay them out + without much white space in between them so they look more contiugous. At + the time of writing, this is used only in the Diffusion repository search + UI. */ +.aphront-list-filter-view-content form + form .phui-form-view { + margin-top: -18px; +} + .aphront-list-filter-view-content .phui-form-view .aphront-form-label { width: 12%; } Index: webroot/rsrc/css/phui/phui-form-view.css =================================================================== --- webroot/rsrc/css/phui/phui-form-view.css +++ webroot/rsrc/css/phui/phui-form-view.css @@ -432,3 +432,21 @@ padding: 16px 0 4px; margin-bottom: 4px; } + +.device-desktop .text-with-submit-control-outer-bounds { + position: relative; +} + +.device-desktop .text-with-submit-control-text-bounds { + position: absolute; + left: 0; + right: 184px; +} + +.device-desktop .text-with-submit-control-submit-bounds { + text-align: right; +} + +.device-desktop .text-with-submit-control-submit { + width: 180px; +}