1
0
Fork 0
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:
epriestley 2011-12-21 09:39:33 -08:00
parent f901befcf5
commit b258095124
9 changed files with 336 additions and 43 deletions

View file

@ -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',

View file

@ -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;
}
}

View file

@ -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');

View file

@ -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

View file

@ -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');

View file

@ -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);
}
}
}
}

View file

@ -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');

View file

@ -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;
}
}

View file

@ -6,6 +6,7 @@
phutil_require_module('phabricator', 'applications/diffusion/request/base');
phutil_require_module('phabricator', 'applications/repository/storage/base');