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 @@ -2639,6 +2639,7 @@ 'PhabricatorTypeaheadCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php', 'PhabricatorTypeaheadDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php', 'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadDatasourceController.php', + 'PhabricatorTypeaheadFunctionHelpController' => 'applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php', 'PhabricatorTypeaheadInvalidTokenException' => 'applications/typeahead/exception/PhabricatorTypeaheadInvalidTokenException.php', 'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php', 'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php', @@ -6054,6 +6055,7 @@ 'PhabricatorTypeaheadCompositeDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorTypeaheadDatasource' => 'Phobject', 'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController', + 'PhabricatorTypeaheadFunctionHelpController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadInvalidTokenException' => 'Exception', 'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource', diff --git a/src/applications/maniphest/typeahead/ManiphestNoOwnerDatasource.php b/src/applications/maniphest/typeahead/ManiphestNoOwnerDatasource.php --- a/src/applications/maniphest/typeahead/ManiphestNoOwnerDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestNoOwnerDatasource.php @@ -20,7 +20,20 @@ public function getDatasourceFunctions() { return array( 'none' => array( - 'name' => pht('Find results which are not assigned.'), + 'name' => pht('No Owner'), + 'summary' => pht('Find results which are not assigned.'), + 'description' => pht( + 'This function includes results which have no owner. Use a query '. + 'like this to find unassigned results:'. + "\n\n". + '> none()'. + "\n\n". + 'If you combine this function with other functions, the query will '. + 'return results which match the other selectors //or// have no '. + 'owner. For example, this query will find results which are owned '. + 'by `alincoln`, and will also find results which have no owner:'. + "\n\n". + '> alincoln, none()'), ), ); } diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php @@ -24,10 +24,54 @@ public function getDatasourceFunctions() { return array( 'any' => array( - 'name' => pht('Find results in any of several projects.'), + 'name' => pht('In Any: ...'), + 'arguments' => pht('project'), + 'summary' => pht('Find results in any of several projects.'), + 'description' => pht( + 'This function allows you to find results in one of several '. + 'projects. Another way to think of this function is that it '. + 'allows you to perform an "or" query.'. + "\n\n". + 'By default, if you enter several projects, results are returned '. + 'only if they belong to all of the projects you enter. That is, '. + 'this query will only return results in //both// projects:'. + "\n\n". + '> ios, android'. + "\n\n". + 'If you want to find results in any of several projects, you can '. + 'use the `any()` function. For example, you can use this query to '. + 'find results which are in //either// project:'. + "\n\n". + '> any(ios), any(android)'. + "\n\n". + 'You can combine the `any()` function with normal project tokens '. + 'to refine results. For example, use this query to find bugs in '. + '//either// iOS or Android:'. + "\n\n". + '> bug, any(ios), any(android)'), ), 'not' => array( - 'name' => pht('Find results not in specific projects.'), + 'name' => pht('Not In: ...'), + 'arguments' => pht('project'), + 'summary' => pht('Find results not in specific projects.'), + 'description' => pht( + 'This function allows you to find results which are not in '. + 'one or more projects. For example, use this query to find '. + 'results which are not associated with a specific project:'. + "\n\n". + '> not(vanilla)'. + "\n\n". + 'You can exclude multiple projects. This will cause the query '. + 'to return only results which are not in any of the excluded '. + 'projects:'. + "\n\n". + '> not(vanilla), not(chocolate)'. + "\n\n". + 'You can combine this function with other functions to refine '. + 'results. For example, use this query to find iOS results which '. + 'are not bugs:'. + "\n\n". + '> ios, not(bug)'), ), ); } diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalUserDatasource.php @@ -24,7 +24,17 @@ public function getDatasourceFunctions() { return array( 'projects' => array( - 'name' => pht("Find results in any of a user's projects."), + 'name' => pht('Projects: ...'), + 'arguments' => pht('username'), + 'summary' => pht("Find results in any of a user's projects."), + 'description' => pht( + 'This function allows you to find results associated with any '. + 'of the projects a specified user is a member of. For example, '. + 'this will find results associated with all of the projects '. + '`alincoln` is a member of:'. + "\n\n". + '> projects(alincoln)'. + "\n\n"), ), ); } diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php @@ -18,7 +18,19 @@ public function getDatasourceFunctions() { return array( 'viewerprojects' => array( - 'name' => pht("Find results in any of the current viewer's projects."), + 'name' => pht("Current Viewer's Projects"), + 'summary' => pht( + "Find results in any of the current viewer's projects."), + 'description' => pht( + "This function matches results in any of the current viewing ". + "user's projects:". + "\n\n". + "> viewerprojects()". + "\n\n". + "This normally means //your// projects, but if you save a query ". + "using this function and send it to someone else, it will mean ". + "//their// projects when they run it (they become the currnet ". + "viewer). This can be useful for building dashboard panels."), ), ); } diff --git a/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php b/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php @@ -24,7 +24,14 @@ public function getDatasourceFunctions() { return array( 'members' => array( - 'name' => pht('Find results for members of a project.'), + 'name' => pht('Members: ...'), + 'arguments' => pht('project'), + 'summary' => pht('Find results for members of a project.'), + 'description' => pht( + 'This function allows you to find results for any of the members '. + 'of a project:'. + "\n\n". + '> members(frontend)'), ), ); } diff --git a/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php b/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php @@ -18,7 +18,15 @@ public function getDatasourceFunctions() { return array( 'null' => array( - 'name' => pht('Find results which are not in any projects.'), + 'name' => pht('Not In Any Projects'), + 'summary' => pht('Find results which are not in any projects.'), + 'description' => pht( + 'This function matches results which are not associated with any '. + 'projects. It is usually most often used to find objects which '. + 'might have slipped through the cracks and not been organized '. + 'properly.'. + "\n\n". + "> null()"), ), ); } diff --git a/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php b/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php --- a/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php +++ b/src/applications/typeahead/application/PhabricatorTypeaheadApplication.php @@ -11,6 +11,8 @@ '/typeahead/' => array( '(?Pbrowse|class)/(?:(?P\w+)/)?' => 'PhabricatorTypeaheadModularDatasourceController', + 'help/(?P\w+)/' + => 'PhabricatorTypeaheadFunctionHelpController', ), ); } diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php new file mode 100644 --- /dev/null +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php @@ -0,0 +1,150 @@ +getViewer(); + $class = $request->getURIData('class'); + + $sources = id(new PhutilSymbolLoader()) + ->setAncestorClass('PhabricatorTypeaheadDatasource') + ->loadObjects(); + if (!isset($sources[$class])) { + return new Aphront404Response(); + } + + $source = $sources[$class]; + + $application_class = $source->getDatasourceApplicationClass(); + if ($application_class) { + $result = id(new PhabricatorApplicationQuery()) + ->setViewer($this->getViewer()) + ->withClasses(array($application_class)) + ->execute(); + if (!$result) { + return new Aphront404Response(); + } + } + + $source->setViewer($viewer); + + $title = pht('Typeahead Function Help'); + + $functions = $source->getAllDatasourceFunctions(); + ksort($functions); + + $content = array(); + + $content[] = '= '.pht('Overview'); + $content[] = pht( + 'Typeahead functions are an advanced feature which allow you to build '. + 'more powerful queries. This document explains functions available '. + 'for the selected control.'. + "\n\n". + 'Note that different controls support //different// functions '. + '(depending on what the control is doing), so these specific functions '. + 'may not work everywhere. You can always check the help for a control '. + 'to review which functions are available for that control.'); + + $table = array(); + + $table_header = array( + pht('Function'), + pht('Token Name'), + pht('Summary'), + ); + $table[] = '| '.implode(' | ', $table_header).' |'; + $table[] = '|---|---|---|'; + + foreach ($functions as $function => $spec) { + $spec = $spec + array( + 'summary' => null, + 'arguments' => null, + ); + + if (idx($spec, 'arguments')) { + $signature = '**'.$function.'(**//'.$spec['arguments'].'//**)**'; + } else { + $signature = '**'.$function.'()**'; + } + + $name = idx($spec, 'name', ''); + $summary = idx($spec, 'summary', ''); + + $table[] = '| '.$signature.' | '.$name.' | '.$summary.' |'; + } + + $table = implode("\n", $table); + $content[] = '= '.pht('Function Quick Reference'); + $content[] = pht( + 'This table briefly describes available functions for this control. '. + 'For details on a particular function, see the corresponding section '. + 'below.'); + $content[] = $table; + + $content[] = '= '.pht('Using Typeahead Functions'); + $content[] = pht( + 'In addition to typing user and project names to build queries, you can '. + 'also type the names of special functions which give you more options '. + 'and the ability to express more complex queries.'. + "\n\n". + 'Functions have an internal name (like `viewer()`) and a '. + 'human-readable name, like `Current Viewer`. In general, you can type '. + 'either one to select the function. You can also click the '. + '{nav icon=search} button on any typeahead control to browse available '. + 'functions and find this documentation.'. + "\n\n". + 'This documentation uses the internal names to make it clear where '. + 'tokens begin and end. Specifically, you will find queries written '. + 'out like this in the documentation: '. + "\n\n". + '> viewer(), alincoln'. + "\n\n". + 'When this query is actually shown in the control, it will look more '. + 'like this:'. + "\n\n". + '> {nav Current Viewer} {nav alincoln (Abraham Lincoln)}'); + + + $middot = "\xC2\xB7"; + foreach ($functions as $function => $spec) { + $arguments = idx($spec, 'arguments', ''); + $name = idx($spec, 'name'); + $content[] = '= '.$function.'('.$arguments.') '.$middot.' '.$name; + $content[] = $spec['description']; + } + + $content = implode("\n\n", $content); + + $content_box = PhabricatorMarkupEngine::renderOneObject( + id(new PhabricatorMarkupOneOff())->setContent($content), + 'default', + $viewer); + + $header = id(new PHUIHeaderView()) + ->setHeader($title); + + $document = id(new PHUIDocumentView()) + ->setHeader($header) + ->setFontKit(PHUIDocumentView::FONT_SOURCE_SANS) + ->appendChild($content_box); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Function Help')); + + return $this->buildApplicationPage( + array( + $crumbs, + $document, + ), + array( + 'title' => $title, + )); + } + +} diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php --- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php @@ -222,11 +222,32 @@ $frame, ); + $function_help = null; + if ($source->getAllDatasourceFunctions()) { + $reference_uri = '/typeahead/help/'.get_class($source).'/'; + + $reference_link = phutil_tag( + 'a', + array( + 'href' => $reference_uri, + 'target' => '_blank', + ), + pht('Reference: Advanced Functions')); + + $function_help = array( + id(new PHUIIconView()) + ->setIconFont('fa-book'), + ' ', + $reference_link, + ); + } + return $this->newDialog() ->setWidth(AphrontDialogView::WIDTH_FORM) ->setRenderDialogAsDiv(true) ->setTitle($source->getBrowseTitle()) ->appendChild($browser) + ->addFooter($function_help) ->addCancelButton('/', pht('Close')); } diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -117,6 +117,14 @@ return $this->usable; } + public function getAllDatasourceFunctions() { + $results = parent::getAllDatasourceFunctions(); + foreach ($this->getUsableDatasources() as $source) { + $results += $source->getAllDatasourceFunctions(); + } + return $results; + } + protected function didEvaluateTokens(array $results) { foreach ($this->getUsableDatasources() as $source) { $results = $source->didEvaluateTokens($results); diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -279,6 +279,14 @@ /** * @task functions */ + public function getAllDatasourceFunctions() { + return $this->getDatasourceFunctions(); + } + + + /** + * @task functions + */ protected function canEvaluateFunction($function) { return $this->shouldStripFunction($function); }