mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-25 15:00:58 +01:00
Include symbols in main typeahead
Summary: - Include symbols in main typeahead results. - Simplify the symbol query a bit and extend PhabricatorOffsetPagedQuery. There was some stuff around language ranking that I got rid of, I think the theory there was that mapping file extensions to languages might not work in general but I think it works fine in practice, and we have more config stuff now around guessing languages and getting the mappings right. - Make it easier to debug the typeahead by showing the results in page format for non-ajax requests. - When we have too many results, show only the top few of each type. Depends on D3116, D3117 Test Plan: Used typeahead, got symbols in results. Hit endpoint with non-ajax, got useful debug view. Reviewers: btrahan, vrana Reviewed By: btrahan CC: aran Maniphest Tasks: T1569 Differential Revision: https://secure.phabricator.com/D3118
This commit is contained in:
parent
bc46e953f7
commit
a476e5c08d
6 changed files with 144 additions and 62 deletions
|
@ -1462,7 +1462,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'javelin-behavior-phabricator-search-typeahead' =>
|
'javelin-behavior-phabricator-search-typeahead' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/f552b264/rsrc/js/application/core/behavior-search-typeahead.js',
|
'uri' => '/res/046ab274/rsrc/js/application/core/behavior-search-typeahead.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -1477,6 +1477,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionSvnRequest' => 'DiffusionRequest',
|
'DiffusionSvnRequest' => 'DiffusionRequest',
|
||||||
'DiffusionSvnTagListQuery' => 'DiffusionTagListQuery',
|
'DiffusionSvnTagListQuery' => 'DiffusionTagListQuery',
|
||||||
'DiffusionSymbolController' => 'DiffusionController',
|
'DiffusionSymbolController' => 'DiffusionController',
|
||||||
|
'DiffusionSymbolQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
'DiffusionTagListController' => 'DiffusionController',
|
'DiffusionTagListController' => 'DiffusionController',
|
||||||
'DiffusionTagListQuery' => 'DiffusionQuery',
|
'DiffusionTagListQuery' => 'DiffusionQuery',
|
||||||
'DiffusionTagListView' => 'DiffusionView',
|
'DiffusionTagListView' => 'DiffusionView',
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
*
|
*
|
||||||
* @group diffusion
|
* @group diffusion
|
||||||
*/
|
*/
|
||||||
final class DiffusionSymbolQuery {
|
final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
|
||||||
|
|
||||||
private $namePrefix;
|
private $namePrefix;
|
||||||
private $name;
|
private $name;
|
||||||
|
@ -36,8 +36,6 @@ final class DiffusionSymbolQuery {
|
||||||
private $language;
|
private $language;
|
||||||
private $type;
|
private $type;
|
||||||
|
|
||||||
private $limit = 20;
|
|
||||||
|
|
||||||
private $needPaths;
|
private $needPaths;
|
||||||
private $needArcanistProject;
|
private $needArcanistProject;
|
||||||
private $needRepositories;
|
private $needRepositories;
|
||||||
|
@ -91,15 +89,6 @@ final class DiffusionSymbolQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @task config
|
|
||||||
*/
|
|
||||||
public function setLimit($limit) {
|
|
||||||
$this->limit = $limit;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @task config
|
* @task config
|
||||||
*/
|
*/
|
||||||
|
@ -145,55 +134,13 @@ final class DiffusionSymbolQuery {
|
||||||
$symbol = new PhabricatorRepositorySymbol();
|
$symbol = new PhabricatorRepositorySymbol();
|
||||||
$conn_r = $symbol->establishConnection('r');
|
$conn_r = $symbol->establishConnection('r');
|
||||||
|
|
||||||
$where = array();
|
|
||||||
if ($this->name) {
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'symbolName = %s',
|
|
||||||
$this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->namePrefix) {
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'symbolName LIKE %>',
|
|
||||||
$this->namePrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->projectIDs) {
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'arcanistProjectID IN (%Ld)',
|
|
||||||
$this->projectIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
$where = 'WHERE ('.implode(') AND (', $where).')';
|
|
||||||
|
|
||||||
$data = queryfx_all(
|
$data = queryfx_all(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'SELECT * FROM %T %Q',
|
'SELECT * FROM %T %Q %Q %Q',
|
||||||
$symbol->getTableName(),
|
$symbol->getTableName(),
|
||||||
$where);
|
$this->buildWhereClause($conn_r),
|
||||||
|
$this->buildOrderClause($conn_r),
|
||||||
// Our ability to match up symbol types and languages probably isn't all
|
$this->buildLimitClause($conn_r));
|
||||||
// that great, so use them as hints for ranking rather than hard
|
|
||||||
// requirements. TODO: Is this really the right choice?
|
|
||||||
foreach ($data as $key => $row) {
|
|
||||||
$score = 0;
|
|
||||||
if ($this->language && $row['symbolLanguage'] == $this->language) {
|
|
||||||
$score += 2;
|
|
||||||
}
|
|
||||||
if ($this->type && $row['symbolType'] == $this->type) {
|
|
||||||
$score += 1;
|
|
||||||
}
|
|
||||||
$data[$key]['score'] = $score;
|
|
||||||
$data[$key]['id'] = $key;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = isort($data, 'score');
|
|
||||||
$data = array_reverse($data);
|
|
||||||
|
|
||||||
$data = array_slice($data, 0, $this->limit);
|
|
||||||
|
|
||||||
$symbols = $symbol->loadAllFromArray($data);
|
$symbols = $symbol->loadAllFromArray($data);
|
||||||
|
|
||||||
|
@ -217,6 +164,54 @@ final class DiffusionSymbolQuery {
|
||||||
/* -( Internals )---------------------------------------------------------- */
|
/* -( Internals )---------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task internal
|
||||||
|
*/
|
||||||
|
private function buildOrderClause($conn_r) {
|
||||||
|
return qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'ORDER BY symbolName ASC');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task internal
|
||||||
|
*/
|
||||||
|
private function buildWhereClause($conn_r) {
|
||||||
|
$where = array();
|
||||||
|
|
||||||
|
if ($this->name) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'symbolName = %s',
|
||||||
|
$this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->namePrefix) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'symbolName LIKE %>',
|
||||||
|
$this->namePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->projectIDs) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'arcanistProjectID IN (%Ld)',
|
||||||
|
$this->projectIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->language) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'symbolLanguage = %s',
|
||||||
|
$this->language);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->formatWhereClause($where);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @task internal
|
* @task internal
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -40,11 +40,13 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||||
$need_upforgrabs = false;
|
$need_upforgrabs = false;
|
||||||
$need_arcanist_projects = false;
|
$need_arcanist_projects = false;
|
||||||
$need_noproject = false;
|
$need_noproject = false;
|
||||||
|
$need_symbols = false;
|
||||||
switch ($this->type) {
|
switch ($this->type) {
|
||||||
case 'mainsearch':
|
case 'mainsearch':
|
||||||
$need_users = true;
|
$need_users = true;
|
||||||
$need_applications = true;
|
$need_applications = true;
|
||||||
$need_rich_data = true;
|
$need_rich_data = true;
|
||||||
|
$need_symbols = true;
|
||||||
break;
|
break;
|
||||||
case 'searchowner':
|
case 'searchowner':
|
||||||
$need_users = true;
|
$need_users = true;
|
||||||
|
@ -238,9 +240,70 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($need_symbols) {
|
||||||
|
$symbols = id(new DiffusionSymbolQuery())
|
||||||
|
->setNamePrefix($query)
|
||||||
|
->setLimit(15)
|
||||||
|
->needArcanistProjects(true)
|
||||||
|
->needRepositories(true)
|
||||||
|
->needPaths(true)
|
||||||
|
->execute();
|
||||||
|
foreach ($symbols as $symbol) {
|
||||||
|
$lang = $symbol->getSymbolLanguage();
|
||||||
|
$name = $symbol->getSymbolName();
|
||||||
|
$type = $symbol->getSymbolType();
|
||||||
|
$proj = $symbol->getArcanistProject()->getName();
|
||||||
|
|
||||||
|
$results[] = id(new PhabricatorTypeaheadResult())
|
||||||
|
->setName($name)
|
||||||
|
->setURI($symbol->getURI())
|
||||||
|
->setPHID(md5($symbol->getURI())) // Just needs to be unique.
|
||||||
|
->setDisplayName($symbol->getName())
|
||||||
|
->setDisplayType(strtoupper($lang).' '.ucwords($type).' ('.$proj.')')
|
||||||
|
->setPriorityType('symb');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$content = mpull($results, 'getWireFormat');
|
$content = mpull($results, 'getWireFormat');
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())->setContent($content);
|
if ($request->isAjax()) {
|
||||||
|
return id(new AphrontAjaxResponse())->setContent($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's a non-Ajax request to this endpoint, show results in a tabular
|
||||||
|
// format to make it easier to debug typeahead output.
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
foreach ($results as $result) {
|
||||||
|
$wire = $result->getWireFormat();
|
||||||
|
foreach ($wire as $k => $v) {
|
||||||
|
$wire[$k] = phutil_escape_html($v);
|
||||||
|
}
|
||||||
|
$rows[] = $wire;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = new AphrontTableView($rows);
|
||||||
|
$table->setHeaders(
|
||||||
|
array(
|
||||||
|
'Name',
|
||||||
|
'URI',
|
||||||
|
'PHID',
|
||||||
|
'Priority',
|
||||||
|
'Display Name',
|
||||||
|
'Display Type',
|
||||||
|
'Image URI',
|
||||||
|
'Priority Type',
|
||||||
|
));
|
||||||
|
|
||||||
|
$panel = new AphrontPanelView();
|
||||||
|
$panel->setHeader('Typeahead Results');
|
||||||
|
$panel->appendChild($table);
|
||||||
|
|
||||||
|
return $this->buildStandardPageResponse(
|
||||||
|
$panel,
|
||||||
|
array(
|
||||||
|
'title' => 'Typeahead Results',
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ final class PhabricatorTypeaheadResult {
|
||||||
$this->priorityString,
|
$this->priorityString,
|
||||||
$this->displayName,
|
$this->displayName,
|
||||||
$this->displayType,
|
$this->displayType,
|
||||||
$this->imageURI,
|
$this->imageURI ? (string)$this->imageURI : null,
|
||||||
$this->priorityType,
|
$this->priorityType,
|
||||||
);
|
);
|
||||||
while (end($data) === null) {
|
while (end($data) === null) {
|
||||||
|
|
|
@ -53,7 +53,8 @@ JX.behavior('phabricator-search-typeahead', function(config) {
|
||||||
var type_priority = {
|
var type_priority = {
|
||||||
// TODO: Put jump nav hits like "D123" first.
|
// TODO: Put jump nav hits like "D123" first.
|
||||||
'apps' : 2,
|
'apps' : 2,
|
||||||
'user' : 3
|
'user' : 3,
|
||||||
|
'symb' : 4
|
||||||
};
|
};
|
||||||
|
|
||||||
var tokens = this.tokenize(value);
|
var tokens = this.tokenize(value);
|
||||||
|
@ -85,9 +86,31 @@ JX.behavior('phabricator-search-typeahead', function(config) {
|
||||||
|
|
||||||
return cmp(u, v);
|
return cmp(u, v);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If we have more results than fit, limit each type of result to 3, so
|
||||||
|
// we show 3 applications, then 3 users, etc.
|
||||||
|
var type_count = 0;
|
||||||
|
var current_type = null;
|
||||||
|
for (var ii = 0; ii < list.length; ii++) {
|
||||||
|
if (list.length <= config.limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (list[ii].type != current_type) {
|
||||||
|
current_type = list[ii].type;
|
||||||
|
type_count = 1;
|
||||||
|
} else {
|
||||||
|
type_count++;
|
||||||
|
if (type_count > 3) {
|
||||||
|
list.splice(ii, 1);
|
||||||
|
ii--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
datasource.setSortHandler(JX.bind(datasource, sort_handler));
|
datasource.setSortHandler(JX.bind(datasource, sort_handler));
|
||||||
|
datasource.setMaximumResultCount(config.limit);
|
||||||
|
|
||||||
var typeahead = new JX.Typeahead(JX.$(config.id), JX.$(config.input));
|
var typeahead = new JX.Typeahead(JX.$(config.id), JX.$(config.input));
|
||||||
typeahead.setDatasource(datasource);
|
typeahead.setDatasource(datasource);
|
||||||
|
|
Loading…
Reference in a new issue