Page MenuHomePhabricator

Allow third-party code to extend the list of functions that function datasources support
Closed, ResolvedPublic


See PHI368. An install would like to be able to specify custom ad-hoc groups using typeahead functions, like people-named-jake().

We can support this by:

  • Lifting QuickSearchEngineExtension from D18760 up into DatasourceEngineExtension.
  • Probably doing the jump nav changes while touching this.
  • Adding a callback for providing additional function datasources.

This API may change when x-y-z() becomes x(y(z())). I plan to consider making this change first but likely give up on it.

Event Timeline

epriestley triaged this task as Normal priority.Feb 14 2018, 4:56 PM
epriestley created this task.
epriestley renamed this task from Modularize typeahead function datasources to Allow third-party code to extend the list of functions that function datasources support.Feb 14 2018, 11:29 PM

@avivey, heads up that I'm renaming PhabricatorQuickSearchEngineExtension to PhabricatorDatasourceEngineExtension. I'm leaving an empty subclass in master for now so that nothing breaks, but will delete it sooner or later, so rename QuickSearch to Datasource in your extends ... statements (and, ideally, class names) at some point before then to keep things working smoothly.

After D19089, you can probably emit a projects(people-named-jake()) token or whatever and have it work for some values of "work" and "whatever". Tentatively, it doesn't look like this needs a lot of API changes, so I'm going to move forward with the simple version of the original request.

Example usage (for simplicity, requires at least one user named "Jake" on your install to function correctly):


final class JakeDatasourceEngineExtension
  extends PhabricatorDatasourceEngineExtension {

  public function newDatasourcesForCompositeDatasource(
    PhabricatorTypeaheadCompositeDatasource $datasource) {

    $can_extend =
      ($datasource instanceof PhabricatorPeopleUserFunctionDatasource) ||
      ($datasource instanceof PhabricatorPeopleOwnerDatasource);

    if (!$can_extend) {
      return array();

    return array(
      new JakeDatasource(),


final class JakeDatasource
  extends PhabricatorTypeaheadDatasource {

  public function getBrowseTitle() {
    return pht('Browse Jakes');

  public function getPlaceholderText() {
    return pht('Type users-named-jake()...');

  public function getDatasourceApplicationClass() {
    return 'PhabricatorPeopleApplication';

  public function getDatasourceFunctions() {
    return array(
      'users-named-jake' => array(
        'name' => pht('Users Named Jake'),
        'summary' => pht('Use users named Jake.'),
        'description' => pht(
          "This function allows you to select all users named Jake."),

  public function loadResults() {
    if ($this->getViewer()->getPHID()) {
      $results = array($this->renderJakeFunctionToken());
    } else {
      $results = array();

    return $this->filterResultsAgainstTokens($results);

  protected function evaluateFunction($function, array $argv_list) {
    $results = array();
    foreach ($argv_list as $argv) {
      foreach ($this->loadJakePHIDs() as $jake_phid) {
        $results[] = $jake_phid;
    return $results;

  public function renderFunctionTokens($function, array $argv_list) {
    $tokens = array();
    foreach ($argv_list as $argv) {
      $tokens[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult(
    return $tokens;

  private function renderJakeFunctionToken() {
    return $this->newFunctionResult()
      ->setName(pht('Users Named Jake'))
      ->addAttribute(pht('Select users named Jake.'));

  private function loadJakePHIDs() {
    $users = id(new PhabricatorUser())->loadAllWhere(
      'realName LIKE %>',
      'Jake ');

    $jake_phids = mpull($users, 'getPHID');

    return array_values($jake_phids);


Screen Shot 2018-02-14 at 5.59.53 PM.png (234×486 px, 17 KB)