mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Expose symbol information over Conduit
Summary: I want to add a command like "where is ArcanistUnitTestEngine" to phabot. I also want to add a symbol typeahead to Diffusion and generally finish up that feature since it's useful but only half-implemented. Consolidate the query logic and expose the data over Conduit. Test Plan: Used /symbol/ and Conduit to lookup symbols. Reviewers: btrahan, jungejason Reviewed By: jungejason CC: aran, jungejason Maniphest Tasks: T315 Differential Revision: https://secure.phabricator.com/D1260
This commit is contained in:
parent
f901befcf5
commit
b258095124
9 changed files with 336 additions and 43 deletions
|
@ -114,6 +114,7 @@ phutil_register_library_map(array(
|
|||
'ConduitAPI_differential_updaterevision_Method' => 'applications/conduit/method/differential/updaterevision',
|
||||
'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'applications/conduit/method/differential/updatetaskrevisionassoc',
|
||||
'ConduitAPI_differential_updateunitresults_Method' => 'applications/conduit/method/differential/updateunitresults',
|
||||
'ConduitAPI_diffusion_findsymbols_Method' => 'applications/conduit/method/diffusion/findsymbols',
|
||||
'ConduitAPI_diffusion_getcommits_Method' => 'applications/conduit/method/diffusion/getcommits',
|
||||
'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'applications/conduit/method/diffusion/getrecentcommitsbypath',
|
||||
'ConduitAPI_feed_publish_Method' => 'applications/conduit/method/feed/publish',
|
||||
|
@ -866,6 +867,7 @@ phutil_register_library_map(array(
|
|||
'ConduitAPI_differential_updaterevision_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_differential_updatetaskrevisionassoc_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_differential_updateunitresults_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_diffusion_findsymbols_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_diffusion_getcommits_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_diffusion_getrecentcommitsbypath_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_feed_publish_Method' => 'ConduitAPIMethod',
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @group conduit
|
||||
*/
|
||||
class ConduitAPI_diffusion_findsymbols_Method extends ConduitAPIMethod {
|
||||
|
||||
public function getMethodDescription() {
|
||||
return "Retrieve Diffusion symbol information.";
|
||||
}
|
||||
|
||||
public function defineParamTypes() {
|
||||
return array(
|
||||
'name' => 'optional string',
|
||||
'namePrefix' => 'optional string',
|
||||
'language' => 'optional string',
|
||||
'type' => 'optional string',
|
||||
);
|
||||
}
|
||||
|
||||
public function defineReturnType() {
|
||||
return 'nonempty list<dict>';
|
||||
}
|
||||
|
||||
public function defineErrorTypes() {
|
||||
return array(
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(ConduitAPIRequest $request) {
|
||||
$name = $request->getValue('name');
|
||||
$name_prefix = $request->getValue('namePrefix');
|
||||
$language = $request->getValue('language');
|
||||
$type = $request->getValue('type');
|
||||
|
||||
$query = new DiffusionSymbolQuery();
|
||||
if ($name !== null) {
|
||||
$query->setName($name);
|
||||
}
|
||||
if ($name_prefix !== null) {
|
||||
$query->setNamePrefix($name_prefix);
|
||||
}
|
||||
if ($language !== null) {
|
||||
$query->setLanguage($language);
|
||||
}
|
||||
if ($type !== null) {
|
||||
$query->setType($type);
|
||||
}
|
||||
|
||||
$query->needPaths(true);
|
||||
$query->needArcanistProjects(true);
|
||||
$query->needRepositories(true);
|
||||
|
||||
$results = $query->execute();
|
||||
|
||||
$response = array();
|
||||
foreach ($results as $result) {
|
||||
$response[] = array(
|
||||
'name' => $result->getSymbolName(),
|
||||
'type' => $result->getSymbolType(),
|
||||
'language' => $result->getSymbolLanguage(),
|
||||
'path' => $result->getPath(),
|
||||
'line' => $result->getLineNumber(),
|
||||
'uri' => PhabricatorEnv::getProductionURI($result->getURI()),
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/conduit/method/base');
|
||||
phutil_require_module('phabricator', 'applications/diffusion/query/symbol');
|
||||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
|
||||
|
||||
phutil_require_source('ConduitAPI_diffusion_findsymbols_Method.php');
|
|
@ -56,6 +56,10 @@ class DiffusionSymbolController extends DiffusionController {
|
|||
}
|
||||
}
|
||||
|
||||
$query->needPaths(true);
|
||||
$query->needArcanistProjects(true);
|
||||
$query->needRepositories(true);
|
||||
|
||||
$symbols = $query->execute();
|
||||
|
||||
// For PHP builtins, jump to php.net documentation.
|
||||
|
@ -70,56 +74,21 @@ class DiffusionSymbolController extends DiffusionController {
|
|||
}
|
||||
}
|
||||
|
||||
if ($symbols) {
|
||||
$projects = id(new PhabricatorRepositoryArcanistProject())->loadAllWhere(
|
||||
'id IN (%Ld)',
|
||||
mpull($symbols, 'getArcanistProjectID', 'getID'));
|
||||
} else {
|
||||
$projects = array();
|
||||
}
|
||||
|
||||
$path_map = array();
|
||||
if ($symbols) {
|
||||
$path_map = queryfx_all(
|
||||
id(new PhabricatorRepository())->establishConnection('r'),
|
||||
'SELECT * FROM %T WHERE id IN (%Ld)',
|
||||
PhabricatorRepository::TABLE_PATH,
|
||||
mpull($symbols, 'getPathID'));
|
||||
$path_map = ipull($path_map, 'path', 'id');
|
||||
}
|
||||
|
||||
$repo_ids = array_filter(mpull($projects, 'getRepositoryID'));
|
||||
if ($repo_ids) {
|
||||
$repos = id(new PhabricatorRepository())->loadAllWhere(
|
||||
'id IN (%Ld)',
|
||||
$repo_ids);
|
||||
} else {
|
||||
$repos = array();
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
foreach ($symbols as $symbol) {
|
||||
$project = idx($projects, $symbol->getArcanistProjectID());
|
||||
$project = $symbol->getArcanistProject();
|
||||
if ($project) {
|
||||
$project_name = $project->getName();
|
||||
} else {
|
||||
$project_name = '-';
|
||||
}
|
||||
|
||||
$file = phutil_escape_html(idx($path_map, $symbol->getPathID()));
|
||||
$file = phutil_escape_html($symbol->getPath());
|
||||
$line = phutil_escape_html($symbol->getLineNumber());
|
||||
|
||||
$repo = idx($repos, $project->getRepositoryID());
|
||||
$repo = $symbol->getRepository();
|
||||
if ($repo) {
|
||||
|
||||
$drequest = DiffusionRequest::newFromAphrontRequestDictionary(
|
||||
array(
|
||||
'callsign' => $repo->getCallsign(),
|
||||
));
|
||||
$branch = $drequest->getBranchURIComponent($drequest->getBranch());
|
||||
$file = $branch.ltrim($file, '/');
|
||||
|
||||
$href = '/diffusion/'.$repo->getCallsign().'/browse/'.$file.'$'.$line;
|
||||
$href = $symbol->getURI();
|
||||
|
||||
if ($request->getBool('jump') && count($symbols) == 1) {
|
||||
// If this is a clickthrough from Differential, just jump them
|
||||
|
|
|
@ -9,10 +9,7 @@
|
|||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||
phutil_require_module('phabricator', 'applications/diffusion/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/diffusion/query/symbol');
|
||||
phutil_require_module('phabricator', 'applications/diffusion/request/base');
|
||||
phutil_require_module('phabricator', 'applications/repository/storage/arcanistproject');
|
||||
phutil_require_module('phabricator', 'applications/repository/storage/repository');
|
||||
phutil_require_module('phabricator', 'storage/queryfx');
|
||||
phutil_require_module('phabricator', 'view/control/table');
|
||||
phutil_require_module('phabricator', 'view/layout/panel');
|
||||
|
||||
|
|
|
@ -16,6 +16,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Query symbol information (class and function names and location), returning
|
||||
* a list of matching @{class:PhabricatorRepositorySymbol} objects and possibly
|
||||
* attached data.
|
||||
*
|
||||
* @task config Configuring the Query
|
||||
* @task exec Executing the Query
|
||||
* @task internal Internals
|
||||
*
|
||||
* @group diffusion
|
||||
*/
|
||||
final class DiffusionSymbolQuery {
|
||||
|
||||
private $namePrefix;
|
||||
|
@ -27,36 +39,101 @@ final class DiffusionSymbolQuery {
|
|||
|
||||
private $limit = 20;
|
||||
|
||||
private $needPaths;
|
||||
private $needArcanistProject;
|
||||
private $needRepositories;
|
||||
|
||||
|
||||
/* -( Configuring the Query )---------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function setNamePrefix($name_prefix) {
|
||||
$this->namePrefix = $name_prefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function setProjectIDs(array $project_ids) {
|
||||
$this->projectIDs = $project_ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function setLanguage($language) {
|
||||
$this->language = $language;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function setType($type) {
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function setLimit($limit) {
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function needPaths($need_paths) {
|
||||
$this->needPaths = $need_paths;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function needArcanistProjects($need_arcanist_projects) {
|
||||
$this->needArcanistProjects = $need_arcanist_projects;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task config
|
||||
*/
|
||||
public function needRepositories($need_repositories) {
|
||||
$this->needRepositories = $need_repositories;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/* -( Executing the Query )------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* @task exec
|
||||
*/
|
||||
public function execute() {
|
||||
if ($this->name && $this->namePrefix) {
|
||||
throw new Exception(
|
||||
|
@ -119,6 +196,85 @@ final class DiffusionSymbolQuery {
|
|||
|
||||
$data = array_slice($data, 0, $this->limit);
|
||||
|
||||
return $symbol->loadAllFromArray($data);
|
||||
$symbols = $symbol->loadAllFromArray($data);
|
||||
|
||||
if ($symbols) {
|
||||
if ($this->needPaths) {
|
||||
$this->loadPaths($symbols);
|
||||
}
|
||||
if ($this->needArcanistProjects || $this->needRepositories) {
|
||||
$this->loadArcanistProjects($symbols);
|
||||
}
|
||||
if ($this->needRepositories) {
|
||||
$this->loadRepositories($symbols);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $symbols;
|
||||
}
|
||||
|
||||
|
||||
/* -( Internals )---------------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task internal
|
||||
*/
|
||||
private function loadPaths(array $symbols) {
|
||||
$path_map = queryfx_all(
|
||||
id(new PhabricatorRepository())->establishConnection('r'),
|
||||
'SELECT * FROM %T WHERE id IN (%Ld)',
|
||||
PhabricatorRepository::TABLE_PATH,
|
||||
mpull($symbols, 'getPathID'));
|
||||
$path_map = ipull($path_map, 'path', 'id');
|
||||
foreach ($symbols as $symbol) {
|
||||
$symbol->attachPath(idx($path_map, $symbol->getPathID()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task internal
|
||||
*/
|
||||
private function loadArcanistProjects(array $symbols) {
|
||||
$projects = id(new PhabricatorRepositoryArcanistProject())->loadAllWhere(
|
||||
'id IN (%Ld)',
|
||||
mpull($symbols, 'getArcanistProjectID'));
|
||||
foreach ($symbols as $symbol) {
|
||||
$project = idx($projects, $symbol->getArcanistProjectID());
|
||||
$symbol->attachArcanistProject($project);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task internal
|
||||
*/
|
||||
private function loadRepositories(array $symbols) {
|
||||
$projects = mpull($symbols, 'getArcanistProject');
|
||||
$projects = array_filter($projects);
|
||||
|
||||
$repo_ids = mpull($projects, 'getRepositoryID');
|
||||
$repo_ids = array_filter($repo_ids);
|
||||
|
||||
if ($repo_ids) {
|
||||
$repos = id(new PhabricatorRepository())->loadAllWhere(
|
||||
'id IN (%Ld)',
|
||||
$repo_ids);
|
||||
} else {
|
||||
$repos = array();
|
||||
}
|
||||
|
||||
foreach ($symbols as $symbol) {
|
||||
$proj = $symbol->getArcanistProject();
|
||||
if ($proj) {
|
||||
$symbol->attachRepository(idx($repos, $proj->getRepositoryID()));
|
||||
} else {
|
||||
$symbol->attachRepository(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/repository/storage/arcanistproject');
|
||||
phutil_require_module('phabricator', 'applications/repository/storage/repository');
|
||||
phutil_require_module('phabricator', 'applications/repository/storage/symbol');
|
||||
phutil_require_module('phabricator', 'storage/qsprintf');
|
||||
phutil_require_module('phabricator', 'storage/queryfx');
|
||||
|
|
|
@ -16,6 +16,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Records information about symbol locations in a codebase, like where classes
|
||||
* and functions are defined.
|
||||
*
|
||||
* Query symbols with @{class:DiffusionSymbolQuery}.
|
||||
*
|
||||
* @group diffusion
|
||||
*/
|
||||
class PhabricatorRepositorySymbol extends PhabricatorRepositoryDAO {
|
||||
|
||||
protected $arcanistProjectID;
|
||||
|
@ -25,6 +34,10 @@ class PhabricatorRepositorySymbol extends PhabricatorRepositoryDAO {
|
|||
protected $pathID;
|
||||
protected $lineNumber;
|
||||
|
||||
private $path = false;
|
||||
private $arcanistProject = false;
|
||||
private $repository = false;
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_IDS => self::IDS_MANUAL,
|
||||
|
@ -32,4 +45,56 @@ class PhabricatorRepositorySymbol extends PhabricatorRepositoryDAO {
|
|||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
$repo = $this->getRepository();
|
||||
$file = $this->getPath();
|
||||
$line = $this->getLineNumber();
|
||||
|
||||
$drequest = DiffusionRequest::newFromAphrontRequestDictionary(
|
||||
array(
|
||||
'callsign' => $repo->getCallsign(),
|
||||
));
|
||||
$branch = $drequest->getBranchURIComponent($drequest->getBranch());
|
||||
$file = $branch.ltrim($file, '/');
|
||||
|
||||
return '/diffusion/'.$repo->getCallsign().'/browse/'.$file.'$'.$line;
|
||||
}
|
||||
|
||||
public function getPath() {
|
||||
if ($this->path === false) {
|
||||
throw new Exception('Call attachPath() before getPath()!');
|
||||
}
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function attachPath($path) {
|
||||
$this->path = $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRepository() {
|
||||
if ($this->repository === false) {
|
||||
throw new Exception('Call attachRepository() before getRepository()!');
|
||||
}
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
public function attachRepository($repository) {
|
||||
$this->repository = $repository;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getArcanistProject() {
|
||||
if ($this->arcanistProject === false) {
|
||||
throw new Exception(
|
||||
'Call attachArcanistProject() before getArcanistProject()!');
|
||||
}
|
||||
return $this->arcanistProject;
|
||||
}
|
||||
|
||||
public function attachArcanistProject($project) {
|
||||
$this->arcanistProject = $project;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/diffusion/request/base');
|
||||
phutil_require_module('phabricator', 'applications/repository/storage/base');
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue