1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 02:02:41 +01:00

Move symbols to be repository-based

Summary: Fixes T7220. Ref T7977. Changes symbols from being bound to an Arcanist project to being bound to a repository.

Test Plan:
- Added symbols and then applied migrations, symbols seemed to be migrated successfully.
- Tested the `/diffusion/symbol/$SYMBOL_NAME` endpoint.
- Tested the `/diffusion/symbol/$SYMBOL_NAME` endpoint with the `?repositories=$REPOSITORY_PHID` parameter.

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: avivey, Korvin, epriestley

Maniphest Tasks: T7977, T7220

Differential Revision: https://secure.phabricator.com/D12608
This commit is contained in:
Joshua Spence 2015-05-03 10:11:17 +10:00
parent 38e89fbb08
commit 2483f6f120
16 changed files with 419 additions and 401 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_repository.repository_symbol
ADD repositoryPHID varbinary(64) NOT NULL AFTER arcanistProjectID;

View file

@ -0,0 +1,26 @@
<?php
$projects = id(new PhabricatorRepositoryArcanistProjectQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->needRepositories(true)
->execute();
$table = new PhabricatorRepositorySymbol();
$conn_w = $table->establishConnection('w');
foreach ($projects as $project) {
$repo = $project->getRepository();
if (!$repo) {
continue;
}
echo pht("Migrating symbols for '%s' project...\n", $project->getName());
queryfx(
$conn_w,
'UPDATE %T SET repositoryPHID = %s WHERE arcanistProjectID = %d',
$table->getTableName(),
$repo->getPHID(),
$project->getID());
}

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_repository.repository_symbol
DROP COLUMN arcanistProjectID;

View file

@ -1,32 +0,0 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$project = id(new PhabricatorRepositoryArcanistProject())->loadOneWhere(
'name = %s', $argv[1]);
if (!$project) {
throw new Exception('No such arcanist project.');
}
$input = file_get_contents('php://stdin');
$normalized = array();
foreach (explode("\n", trim($input)) as $path) {
// emulate the behavior of the symbol generation scripts
$normalized[] = '/'.ltrim($path, './');
}
$paths = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(
$normalized);
$symbol = new PhabricatorRepositorySymbol();
$conn_w = $symbol->establishConnection('w');
foreach (array_chunk(array_values($paths), 128) as $chunk) {
queryfx(
$conn_w,
'DELETE FROM %T WHERE arcanistProjectID = %d AND pathID IN (%Ld)',
$symbol->getTableName(),
$project->getID(),
$chunk);
}

View file

@ -0,0 +1,58 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**clear_repository_symbols.php** [__options__] __callsign__
Clear repository symbols.
EOSYNOPSIS
);
$args->parseStandardArguments();
$args->parse(
array(
array(
'name' => 'callsign',
'wildcard' => true,
),
));
$callsigns = $args->getArg('callsign');
if (count($callsigns) !== 1) {
$args->printHelpAndExit();
}
$callsign = head($callsigns);
$repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withCallsigns($callsigns)
->executeOne();
if (!$repository) {
echo pht("Repository '%s' does not exist.", $callsign);
exit(1);
}
$input = file_get_contents('php://stdin');
$normalized = array();
foreach (explode("\n", trim($input)) as $path) {
// Emulate the behavior of the symbol generation scripts.
$normalized[] = '/'.ltrim($path, './');
}
$paths = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(
$normalized);
$symbol = new PhabricatorRepositorySymbol();
$conn_w = $symbol->establishConnection('w');
foreach (array_chunk(array_values($paths), 128) as $chunk) {
queryfx(
$conn_w,
'DELETE FROM %T WHERE repositoryPHID = %s AND pathID IN (%Ld)',
$symbol->getTableName(),
$repository->getPHID(),
$chunk);
}

View file

@ -4,79 +4,94 @@
$root = dirname(dirname(dirname(__FILE__))); $root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php'; require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**generate_ctags_symbols.php** [__options__]
Generate repository symbols using Exuberant Ctags. Paths are read from stdin.
EOSYNOPSIS
);
$args->parseStandardArguments();
if (ctags_check_executable() == false) { if (ctags_check_executable() == false) {
echo phutil_console_format( echo phutil_console_format(
"Could not find Exuberant ctags. Make sure it is installed and\n". "%s\n\n%s\n",
"available in executable path.\n\n". pht(
"Exuberant ctags project page: http://ctags.sourceforge.net/\n"); 'Could not find Exuberant Ctags. Make sure it is installed and '.
'available in executable path.'),
pht(
'Exuberant Ctags project page: %s',
'http://ctags.sourceforge.net/'));
exit(1); exit(1);
} }
if ($argc !== 1 || posix_isatty(STDIN)) { if (posix_isatty(STDIN)) {
echo phutil_console_format( echo phutil_console_format(
"usage: find . -type f -name '*.py' | ./generate_ctags_symbols.php\n"); "%s\n",
pht(
'Usage: %s',
"find . -type f -name '*.py' | ./generate_ctags_symbols.php"));
exit(1); exit(1);
} }
$input = file_get_contents('php://stdin'); $input = file_get_contents('php://stdin');
$input = trim($input);
$input = explode("\n", $input);
$data = array(); $data = array();
$futures = array(); $futures = array();
foreach ($input as $file) { foreach (explode("\n", trim($input)) as $file) {
$file = Filesystem::readablePath($file); $file = Filesystem::readablePath($file);
$futures[$file] = ctags_get_parser_future($file); $futures[$file] = ctags_get_parser_future($file);
} }
$futures = id(new FutureIterator($futures)) $futures = new FutureIterator($futures);
->limit(8); foreach ($futures->limit(8) as $file => $future) {
foreach ($futures as $file => $future) {
$tags = $future->resolve(); $tags = $future->resolve();
$tags = explode("\n", $tags[1]); $tags = explode("\n", $tags[1]);
foreach ($tags as $tag) { foreach ($tags as $tag) {
$parts = explode(';', $tag); $parts = explode(';', $tag);
// skip lines that we can not parse
// Skip lines that we can not parse.
if (count($parts) < 2) { if (count($parts) < 2) {
continue; continue;
} }
// split ctags information // Split ctags information.
$tag_info = explode("\t", $parts[0]); $tag_info = explode("\t", $parts[0]);
// split exuberant ctags "extension fields" (additional information)
// Split exuberant ctags "extension fields" (additional information).
$parts[1] = trim($parts[1], "\t \""); $parts[1] = trim($parts[1], "\t \"");
$extension_fields = explode("\t", $parts[1]); $extension_fields = explode("\t", $parts[1]);
// skip lines that we can not parse // Skip lines that we can not parse.
if (count($tag_info) < 3 || count($extension_fields) < 2) { if (count($tag_info) < 3 || count($extension_fields) < 2) {
continue; continue;
} }
// default $context to empty // Default context to empty.
$extension_fields[] = ''; $extension_fields[] = '';
list($token, $file_path, $line_num) = $tag_info; list($token, $file_path, $line_num) = $tag_info;
list($type, $language, $context) = $extension_fields; list($type, $language, $context) = $extension_fields;
// skip lines with tokens containing a space // Skip lines with tokens containing a space.
if (strpos($token, ' ') !== false) { if (strpos($token, ' ') !== false) {
continue; continue;
} }
// strip "language:" // Strip "language:"
$language = substr($language, 9); $language = substr($language, 9);
// To keep consistent with "Separate with commas, for example: php, py" // To keep consistent with "Separate with commas, for example: php, py"
// in Arcanist Project edit form. // in Arcanist Project edit form.
$language = str_ireplace('python', 'py', $language); $language = str_ireplace('python', 'py', $language);
// also, "normalize" c++ and c# // Also, "normalize" C++ and C#.
$language = str_ireplace('c++', 'cpp', $language); $language = str_ireplace('c++', 'cpp', $language);
$language = str_ireplace('c#', 'cs', $language); $language = str_ireplace('c#', 'cs', $language);
// Ruby has "singleton method", for example // Ruby has "singleton method", for example.
$type = substr(str_replace(' ', '_', $type), 0, 12); $type = substr(str_replace(' ', '_', $type), 0, 12);
// class:foo, struct:foo, union:foo, enum:foo, ... // class:foo, struct:foo, union:foo, enum:foo, ...
$context = last(explode(':', $context, 2)); $context = last(explode(':', $context, 2));
@ -89,25 +104,18 @@ foreach ($futures as $file => $future) {
} }
} }
function ctags_get_parser_future($file_path) { function ctags_get_parser_future($path) {
$future = new ExecFuture('ctags -n --fields=Kls -o - %s', $future = new ExecFuture('ctags -n --fields=Kls -o - %s', $path);
$file_path);
return $future; return $future;
} }
function ctags_check_executable() { function ctags_check_executable() {
$future = new ExecFuture('ctags --version'); $result = exec_manual('ctags --version');
$result = $future->resolve(); return !empty($result[1]);
if (empty($result[1])) {
return false;
}
return true;
} }
function print_symbol($file, $line_num, $type, $token, $context, $language) { function print_symbol($file, $line_num, $type, $token, $context, $language) {
// get rid of relative path // Get rid of relative path.
$file = explode('/', $file); $file = explode('/', $file);
if ($file[0] == '.' || $file[0] == '..') { if ($file[0] == '.' || $file[0] == '..') {
array_shift($file); array_shift($file);

View file

@ -4,28 +4,36 @@
$root = dirname(dirname(dirname(__FILE__))); $root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php'; require_once $root.'/scripts/__init_script__.php';
if ($argc !== 1 || posix_isatty(STDIN)) { $args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**generate_php_symbols.php** [__options__]
Generate repository symbols using XHPAST. Paths are read from stdin.
EOSYNOPSIS
);
$args->parseStandardArguments();
if (posix_isatty(STDIN)) {
echo phutil_console_format( echo phutil_console_format(
"usage: find . -type f -name '*.php' | ./generate_php_symbols.php\n"); "%s\n",
pht(
'Usage: %s',
"find . -type f -name '*.php' | ./generate_php_symbols.php"));
exit(1); exit(1);
} }
$input = file_get_contents('php://stdin'); $input = file_get_contents('php://stdin');
$input = trim($input);
$input = explode("\n", $input);
$data = array(); $data = array();
$futures = array(); $futures = array();
foreach ($input as $file) { foreach (explode("\n", trim($input)) as $file) {
$file = Filesystem::readablePath($file); $file = Filesystem::readablePath($file);
$data[$file] = Filesystem::readFile($file); $data[$file] = Filesystem::readFile($file);
$futures[$file] = PhutilXHPASTBinary::getParserFuture($data[$file]); $futures[$file] = PhutilXHPASTBinary::getParserFuture($data[$file]);
} }
$futures = id(new FutureIterator($futures)) $futures = new FutureIterator($futures);
->limit(8); foreach ($futures->limit(8) as $file => $future) {
foreach ($futures as $file => $future) {
$tree = XHPASTTree::newFromDataAndResolvedExecFuture( $tree = XHPASTTree::newFromDataAndResolvedExecFuture(
$data[$file], $data[$file],
$future->resolve()); $future->resolve());
@ -36,7 +44,7 @@ foreach ($futures as $file => $future) {
$functions = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION'); $functions = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION');
foreach ($functions as $function) { foreach ($functions as $function) {
$name = $function->getChildByIndex(2); $name = $function->getChildByIndex(2);
// Skip anonymous functions // Skip anonymous functions.
if (!$name->getConcreteString()) { if (!$name->getConcreteString()) {
continue; continue;
} }
@ -67,8 +75,8 @@ foreach ($futures as $file => $future) {
} }
foreach ($scopes as $scope) { foreach ($scopes as $scope) {
// this prints duplicate symbols in the case of nested classes // This prints duplicate symbols in the case of nested classes.
// luckily, PHP doesn't allow those // Luckily, PHP doesn't allow those.
list($class, $class_name) = $scope; list($class, $class_name) = $scope;
$consts = $class->selectDescendantsOfType( $consts = $class->selectDescendantsOfType(
@ -100,15 +108,15 @@ foreach ($futures as $file => $future) {
} }
} }
function print_symbol($file, $type, $token, $context = null) { function print_symbol($file, $type, XHPASTNode $node, $context = null) {
$parts = array( $parts = array(
$context ? $context->getConcreteString() : '', $context ? $context->getConcreteString() : '',
// variable tokens are `$name`, not just `name`, so strip the $ off of // Variable tokens are `$name`, not just `name`, so strip the "$"" off of
// class field names // class field names
ltrim($token->getConcreteString(), '$'), ltrim($node->getConcreteString(), '$'),
$type, $type,
'php', 'php',
$token->getLineNumber(), $node->getLineNumber(),
'/'.ltrim($file, './'), '/'.ltrim($file, './'),
); );
echo implode(' ', $parts)."\n"; echo implode(' ', $parts)."\n";

View file

@ -1,205 +0,0 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**import_project_symbols.php** [__options__] __project_name__ < symbols
Import project symbols (symbols are read from stdin).
EOSYNOPSIS
);
$args->parseStandardArguments();
$args->parse(
array(
array(
'name' => 'no-purge',
'help' => 'Do not clear all symbols for this project before '.
'uploading new symbols. Useful for incremental updating.',
),
array(
'name' => 'ignore-errors',
'help' => 'If a line can\'t be parsed, ignore that line and '.
'continue instead of exiting.',
),
array(
'name' => 'max-transaction',
'param' => 'num-syms',
'default' => '100000',
'help' => 'Maximum number of symbols that should '.
'be part of a single transaction',
),
array(
'name' => 'more',
'wildcard' => true,
),
));
$more = $args->getArg('more');
if (count($more) !== 1) {
$args->printHelpAndExit();
}
$project_name = head($more);
$project = id(new PhabricatorRepositoryArcanistProject())->loadOneWhere(
'name = %s',
$project_name);
if (!$project) {
// TODO: Provide a less silly way to do this explicitly, or just do it right
// here.
echo "Project '{$project_name}' is unknown. Upload a diff to implicitly ".
"create it.\n";
exit(1);
}
echo "Parsing input from stdin...\n";
$input = file_get_contents('php://stdin');
$input = trim($input);
$input = explode("\n", $input);
function commit_symbols ($syms, $project, $no_purge) {
echo "Looking up path IDs...\n";
$path_map =
PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(
ipull($syms, 'path'));
$symbol = new PhabricatorRepositorySymbol();
$conn_w = $symbol->establishConnection('w');
echo "Preparing queries...\n";
$sql = array();
foreach ($syms as $dict) {
$sql[] = qsprintf(
$conn_w,
'(%d, %s, %s, %s, %s, %d, %d)',
$project->getID(),
$dict['ctxt'],
$dict['name'],
$dict['type'],
$dict['lang'],
$dict['line'],
$path_map[$dict['path']]);
}
if (!$no_purge) {
echo "Purging old syms...\n";
queryfx($conn_w,
'DELETE FROM %T WHERE arcanistProjectID = %d',
$symbol->getTableName(),
$project->getID());
}
echo "Loading ".number_format(count($sql))." syms...\n";
foreach (array_chunk($sql, 128) as $chunk) {
queryfx($conn_w,
'INSERT INTO %T
(arcanistProjectID, symbolContext, symbolName, symbolType,
symbolLanguage, lineNumber, pathID) VALUES %Q',
$symbol->getTableName(),
implode(', ', $chunk));
}
}
function check_string_value($value, $field_name, $line_no, $max_length) {
if (strlen($value) > $max_length) {
throw new Exception(
"{$field_name} '{$value}' defined on line #{$line_no} is too long, ".
"maximum {$field_name} length is {$max_length} characters.");
}
if (!phutil_is_utf8_with_only_bmp_characters($value)) {
throw new Exception(
"{$field_name} '{$value}' defined on line #{$line_no} is not a valid ".
"UTF-8 string, ".
"it should contain only UTF-8 characters.");
}
}
$no_purge = $args->getArg('no-purge');
$symbols = array();
foreach ($input as $key => $line) {
try {
$line_no = $key + 1;
$matches = null;
$ok = preg_match(
'/^((?P<context>[^ ]+)? )?(?P<name>[^ ]+) (?P<type>[^ ]+) '.
'(?P<lang>[^ ]+) (?P<line>\d+) (?P<path>.*)$/',
$line,
$matches);
if (!$ok) {
throw new Exception(
"Line #{$line_no} of input is invalid. Expected five or six ".
"space-delimited fields: maybe symbol context, symbol name, symbol ".
"type, symbol language, line number, path. ".
"For example:\n\n".
"idx function php 13 /path/to/some/file.php\n\n".
"Actual line was:\n\n".
"{$line}");
}
if (empty($matches['context'])) {
$matches['context'] = '';
}
$context = $matches['context'];
$name = $matches['name'];
$type = $matches['type'];
$lang = $matches['lang'];
$line_number = $matches['line'];
$path = $matches['path'];
check_string_value($context, 'Symbol context', $line_no, 128);
check_string_value($name, 'Symbol name', $line_no, 128);
check_string_value($type, 'Symbol type', $line_no, 12);
check_string_value($lang, 'Symbol language', $line_no, 32);
check_string_value($path, 'Path', $line_no, 512);
if (!strlen($path) || $path[0] != '/') {
throw new Exception(
"Path '{$path}' defined on line #{$line_no} is invalid. Paths should ".
"begin with '/' and specify a path from the root of the project, like ".
"'/src/utils/utils.php'.");
}
$symbols[] = array(
'ctxt' => $context,
'name' => $name,
'type' => $type,
'lang' => $lang,
'line' => $line_number,
'path' => $path,
);
} catch (Exception $e) {
if ($args->getArg('ignore-errors')) {
continue;
} else {
throw $e;
}
}
if (count ($symbols) >= $args->getArg('max-transaction')) {
try {
echo "Committing {$args->getArg('max-transaction')} symbols....\n";
commit_symbols($symbols, $project, $no_purge);
$no_purge = true;
unset($symbols);
$symbols = array();
} catch (Exception $e) {
if ($args->getArg('ignore-errors')) {
continue;
} else {
throw $e;
}
}
}
}
if (count($symbols)) {
commit_symbols($symbols, $project, $no_purge);
}
echo "Done.\n";

View file

@ -0,0 +1,228 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**import_repository_symbols.php** [__options__] __callsign__ < symbols
Import repository symbols (symbols are read from stdin).
EOSYNOPSIS
);
$args->parseStandardArguments();
$args->parse(
array(
array(
'name' => 'no-purge',
'help' => pht(
'Do not clear all symbols for this repository before '.
'uploading new symbols. Useful for incremental updating.'),
),
array(
'name' => 'ignore-errors',
'help' => pht(
"If a line can't be parsed, ignore that line and ".
"continue instead of exiting."),
),
array(
'name' => 'max-transaction',
'param' => 'num-syms',
'default' => '100000',
'help' => pht(
'Maximum number of symbols that should '.
'be part of a single transaction.'),
),
array(
'name' => 'more',
'wildcard' => true,
),
));
$more = $args->getArg('more');
if (count($more) !== 1) {
$args->printHelpAndExit();
}
$callsign = head($more);
$repository = id(new PhabricatorRepository())->loadOneWhere(
'callsign = %s',
$callsign);
if (!$repository) {
echo pht("Repository '%s' does not exist.", $callsign);
exit(1);
}
if (!function_exists('posix_isatty') || posix_isatty(STDIN)) {
echo pht('Parsing input from stdin...'), "\n";
}
$input = file_get_contents('php://stdin');
$input = trim($input);
$input = explode("\n", $input);
function commit_symbols(
array $symbols,
PhabricatorRepository $repository,
$no_purge) {
echo pht('Looking up path IDs...'), "\n";
$path_map =
PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(
ipull($symbols, 'path'));
$symbol = new PhabricatorRepositorySymbol();
$conn_w = $symbol->establishConnection('w');
echo pht('Preparing queries...'), "\n";
$sql = array();
foreach ($symbols as $dict) {
$sql[] = qsprintf(
$conn_w,
'(%s, %s, %s, %s, %s, %d, %d)',
$repository->getPHID(),
$dict['ctxt'],
$dict['name'],
$dict['type'],
$dict['lang'],
$dict['line'],
$path_map[$dict['path']]);
}
if (!$no_purge) {
echo pht('Purging old symbols...'), "\n";
queryfx(
$conn_w,
'DELETE FROM %T WHERE repositoryPHID = %s',
$symbol->getTableName(),
$repository->getPHID());
}
echo pht('Loading %s symbols...', new PhutilNumber(count($sql))), "\n";
foreach (array_chunk($sql, 128) as $chunk) {
queryfx(
$conn_w,
'INSERT INTO %T
(repositoryPHID, symbolContext, symbolName, symbolType,
symbolLanguage, lineNumber, pathID) VALUES %Q',
$symbol->getTableName(),
implode(', ', $chunk));
}
}
function check_string_value($value, $field_name, $line_no, $max_length) {
if (strlen($value) > $max_length) {
throw new Exception(
pht(
"%s '%s' defined on line #%d is too long, ".
"maximum %s length is %d characters.",
$field_name,
$value,
$line_no,
$field_name,
$max_length));
}
if (!phutil_is_utf8_with_only_bmp_characters($value)) {
throw new Exception(
pht(
"%s '%s' defined on line #%d is not a valid ".
"UTF-8 string, it should contain only UTF-8 characters.",
$field_name,
$value,
$line_no));
}
}
$no_purge = $args->getArg('no-purge');
$symbols = array();
foreach ($input as $key => $line) {
try {
$line_no = $key + 1;
$matches = null;
$ok = preg_match(
'/^((?P<context>[^ ]+)? )?(?P<name>[^ ]+) (?P<type>[^ ]+) '.
'(?P<lang>[^ ]+) (?P<line>\d+) (?P<path>.*)$/',
$line,
$matches);
if (!$ok) {
throw new Exception(
pht(
"Line #%d of input is invalid. Expected five or six space-delimited ".
"fields: maybe symbol context, symbol name, symbol type, symbol ".
"language, line number, path. For example:\n\n%s\n\n".
"Actual line was:\n\n%s",
$line_no,
'idx function php 13 /path/to/some/file.php',
$line));
}
if (empty($matches['context'])) {
$matches['context'] = '';
}
$context = $matches['context'];
$name = $matches['name'];
$type = $matches['type'];
$lang = $matches['lang'];
$line_number = $matches['line'];
$path = $matches['path'];
check_string_value($context, 'Symbol context', $line_no, 128);
check_string_value($name, 'Symbol name', $line_no, 128);
check_string_value($type, 'Symbol type', $line_no, 12);
check_string_value($lang, 'Symbol language', $line_no, 32);
check_string_value($path, 'Path', $line_no, 512);
if (!strlen($path) || $path[0] != '/') {
throw new Exception(
pht(
"Path '%s' defined on line #%d is invalid. Paths should begin with ".
"'%s' and specify a path from the root of the project, like '%s'.",
$path,
$line_no,
'/',
'/src/utils/utils.php'));
}
$symbols[] = array(
'ctxt' => $context,
'name' => $name,
'type' => $type,
'lang' => $lang,
'line' => $line_number,
'path' => $path,
);
} catch (Exception $e) {
if ($args->getArg('ignore-errors')) {
continue;
} else {
throw $e;
}
}
if (count ($symbols) >= $args->getArg('max-transaction')) {
try {
echo pht(
"Committing %s symbols...\n",
new PhutilNumber($args->getArg('max-transaction')));
commit_symbols($symbols, $repository, $no_purge);
$no_purge = true;
unset($symbols);
$symbols = array();
} catch (Exception $e) {
if ($args->getArg('ignore-errors')) {
continue;
} else {
throw $e;
}
}
}
}
if (count($symbols)) {
commit_symbols($symbols, $repository, $no_purge);
}
echo pht('Done.'), "\n";

View file

@ -8,7 +8,7 @@ final class DiffusionFindSymbolsConduitAPIMethod
} }
public function getMethodDescription() { public function getMethodDescription() {
return 'Retrieve Diffusion symbol information.'; return pht('Retrieve Diffusion symbol information.');
} }
protected function defineParamTypes() { protected function defineParamTypes() {
@ -51,7 +51,6 @@ final class DiffusionFindSymbolsConduitAPIMethod
} }
$query->needPaths(true); $query->needPaths(true);
$query->needArcanistProjects(true);
$query->needRepositories(true); $query->needRepositories(true);
$results = $query->execute(); $results = $query->execute();

View file

@ -24,25 +24,25 @@ final class DiffusionSymbolController extends DiffusionController {
$query->setLanguage($request->getStr('lang')); $query->setLanguage($request->getStr('lang'));
} }
if ($request->getStr('projects')) { if ($request->getStr('repositories')) {
$phids = $request->getStr('projects'); $phids = $request->getStr('repositories');
$phids = explode(',', $phids); $phids = explode(',', $phids);
$phids = array_filter($phids); $phids = array_filter($phids);
if ($phids) { if ($phids) {
$projects = id(new PhabricatorRepositoryArcanistProject()) $repos = id(new PhabricatorRepositoryQuery())
->loadAllWhere( ->setViewer($request->getUser())
'phid IN (%Ls)', ->withPHIDs($phids)
$phids); ->execute();
$projects = mpull($projects, 'getID');
if ($projects) { $repos = mpull($repos, 'getPHID');
$query->setProjectIDs($projects); if ($repos) {
$query->withRepositoryPHIDs($repos);
} }
} }
} }
$query->needPaths(true); $query->needPaths(true);
$query->needArcanistProjects(true);
$query->needRepositories(true); $query->needRepositories(true);
$symbols = $query->execute(); $symbols = $query->execute();
@ -73,13 +73,6 @@ final class DiffusionSymbolController extends DiffusionController {
$rows = array(); $rows = array();
foreach ($symbols as $symbol) { foreach ($symbols as $symbol) {
$project = $symbol->getArcanistProject();
if ($project) {
$project_name = $project->getName();
} else {
$project_name = '-';
}
$file = $symbol->getPath(); $file = $symbol->getPath();
$line = $symbol->getLineNumber(); $line = $symbol->getLineNumber();
@ -110,7 +103,7 @@ final class DiffusionSymbolController extends DiffusionController {
$symbol->getSymbolContext(), $symbol->getSymbolContext(),
$symbol->getSymbolName(), $symbol->getSymbolName(),
$symbol->getSymbolLanguage(), $symbol->getSymbolLanguage(),
$project_name, $repo->getMonogram(),
$location, $location,
); );
} }
@ -122,7 +115,7 @@ final class DiffusionSymbolController extends DiffusionController {
pht('Context'), pht('Context'),
pht('Name'), pht('Name'),
pht('Language'), pht('Language'),
pht('Project'), pht('Repository'),
pht('File'), pht('File'),
)); ));
$table->setColumnClasses( $table->setColumnClasses(

View file

@ -16,12 +16,11 @@ final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
private $namePrefix; private $namePrefix;
private $name; private $name;
private $projectIDs; private $repositoryPHIDs;
private $language; private $language;
private $type; private $type;
private $needPaths; private $needPaths;
private $needArcanistProject;
private $needRepositories; private $needRepositories;
@ -72,8 +71,8 @@ final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
/** /**
* @task config * @task config
*/ */
public function setProjectIDs(array $project_ids) { public function withRepositoryPHIDs(array $repository_phids) {
$this->projectIDs = $project_ids; $this->repositoryPHIDs = $repository_phids;
return $this; return $this;
} }
@ -105,15 +104,6 @@ final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
} }
/**
* @task config
*/
public function needArcanistProjects($need_arcanist_projects) {
$this->needArcanistProjects = $need_arcanist_projects;
return $this;
}
/** /**
* @task config * @task config
*/ */
@ -132,10 +122,10 @@ final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
public function execute() { public function execute() {
if ($this->name && $this->namePrefix) { if ($this->name && $this->namePrefix) {
throw new Exception( throw new Exception(
'You can not set both a name and a name prefix!'); pht('You can not set both a name and a name prefix!'));
} else if (!$this->name && !$this->namePrefix) { } else if (!$this->name && !$this->namePrefix) {
throw new Exception( throw new Exception(
'You must set a name or a name prefix!'); pht('You must set a name or a name prefix!'));
} }
$symbol = new PhabricatorRepositorySymbol(); $symbol = new PhabricatorRepositorySymbol();
@ -155,9 +145,6 @@ final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
if ($this->needPaths) { if ($this->needPaths) {
$this->loadPaths($symbols); $this->loadPaths($symbols);
} }
if ($this->needArcanistProjects || $this->needRepositories) {
$this->loadArcanistProjects($symbols);
}
if ($this->needRepositories) { if ($this->needRepositories) {
$this->loadRepositories($symbols); $this->loadRepositories($symbols);
} }
@ -208,11 +195,11 @@ final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
$this->namePrefix); $this->namePrefix);
} }
if ($this->projectIDs) { if ($this->repositoryPHIDs) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,
'arcanistProjectID IN (%Ld)', 'repositoryPHID IN (%Ls)',
$this->projectIDs); $this->repositoryPHIDs);
} }
if ($this->language) { if ($this->language) {
@ -250,49 +237,21 @@ final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
} }
/**
* @task internal
*/
private function loadArcanistProjects(array $symbols) {
assert_instances_of($symbols, 'PhabricatorRepositorySymbol');
$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 * @task internal
*/ */
private function loadRepositories(array $symbols) { private function loadRepositories(array $symbols) {
assert_instances_of($symbols, 'PhabricatorRepositorySymbol'); assert_instances_of($symbols, 'PhabricatorRepositorySymbol');
$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 PhabricatorRepositoryQuery()) $repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer()) ->setViewer($this->viewer)
->withIDs($repo_ids) ->withPHIDs(mpull($symbols, 'getRepositoryPHID'))
->execute(); ->execute();
} else { $repos = mpull($repos, null, 'getPHID');
$repos = array();
}
foreach ($symbols as $symbol) { foreach ($symbols as $symbol) {
$proj = $symbol->getArcanistProject(); $repository = idx($repos, $symbol->getRepositoryPHID());
if ($proj) { $symbol->attachRepository($repository);
$symbol->attachRepository(idx($repos, $proj->getRepositoryID()));
} else {
$symbol->attachRepository(null);
}
} }
} }

View file

@ -32,7 +32,6 @@ final class DiffusionSymbolDatasource
->setViewer($viewer) ->setViewer($viewer)
->setNamePrefix($raw_query) ->setNamePrefix($raw_query)
->setLimit(15) ->setLimit(15)
->needArcanistProjects(true)
->needRepositories(true) ->needRepositories(true)
->needPaths(true) ->needPaths(true)
->execute(); ->execute();
@ -40,14 +39,14 @@ final class DiffusionSymbolDatasource
$lang = $symbol->getSymbolLanguage(); $lang = $symbol->getSymbolLanguage();
$name = $symbol->getSymbolName(); $name = $symbol->getSymbolName();
$type = $symbol->getSymbolType(); $type = $symbol->getSymbolType();
$proj = $symbol->getArcanistProject()->getName(); $repo = $symbol->getRepository()->getName();
$results[] = id(new PhabricatorTypeaheadResult()) $results[] = id(new PhabricatorTypeaheadResult())
->setName($name) ->setName($name)
->setURI($symbol->getURI()) ->setURI($symbol->getURI())
->setPHID(md5($symbol->getURI())) // Just needs to be unique. ->setPHID(md5($symbol->getURI())) // Just needs to be unique.
->setDisplayName($name) ->setDisplayName($name)
->setDisplayType(strtoupper($lang).' '.ucwords($type).' ('.$proj.')') ->setDisplayType(strtoupper($lang).' '.ucwords($type).' ('.$repo.')')
->setPriorityType('symb'); ->setPriorityType('symb');
} }
} }

View file

@ -1163,10 +1163,15 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
$projects = id(new PhabricatorRepositoryArcanistProject()) $projects = id(new PhabricatorRepositoryArcanistProject())
->loadAllWhere('repositoryID = %d', $this->getID()); ->loadAllWhere('repositoryID = %d', $this->getID());
foreach ($projects as $project) { foreach ($projects as $project) {
// note each project deletes its PhabricatorRepositorySymbols
$project->delete(); $project->delete();
} }
queryfx(
$this->establishConnection('w'),
'DELETE FROM %T WHERE repositoryPHID = %s',
id(new PhabricatorRepositorySymbol())->getTableName(),
$this->getPHID());
$commits = id(new PhabricatorRepositoryCommit()) $commits = id(new PhabricatorRepositoryCommit())
->loadAllWhere('repositoryID = %d', $this->getID()); ->loadAllWhere('repositoryID = %d', $this->getID());
foreach ($commits as $commit) { foreach ($commits as $commit) {

View file

@ -45,20 +45,6 @@ final class PhabricatorRepositoryArcanistProject
PhabricatorRepositoryArcanistProjectPHIDType::TYPECONST); PhabricatorRepositoryArcanistProjectPHIDType::TYPECONST);
} }
public function delete() {
$this->openTransaction();
queryfx(
$this->establishConnection('w'),
'DELETE FROM %T WHERE arcanistProjectID = %d',
id(new PhabricatorRepositorySymbol())->getTableName(),
$this->getID());
$result = parent::delete();
$this->saveTransaction();
return $result;
}
public function getRepository() { public function getRepository() {
return $this->assertAttached($this->repository); return $this->assertAttached($this->repository);
} }

View file

@ -8,7 +8,7 @@
*/ */
final class PhabricatorRepositorySymbol extends PhabricatorRepositoryDAO { final class PhabricatorRepositorySymbol extends PhabricatorRepositoryDAO {
protected $arcanistProjectID; protected $repositoryPHID;
protected $symbolContext; protected $symbolContext;
protected $symbolName; protected $symbolName;
protected $symbolType; protected $symbolType;
@ -17,12 +17,10 @@ final class PhabricatorRepositorySymbol extends PhabricatorRepositoryDAO {
protected $lineNumber; protected $lineNumber;
private $path = self::ATTACHABLE; private $path = self::ATTACHABLE;
private $arcanistProject = self::ATTACHABLE;
private $repository = self::ATTACHABLE; private $repository = self::ATTACHABLE;
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
self::CONFIG_IDS => self::IDS_MANUAL,
self::CONFIG_TIMESTAMPS => false, self::CONFIG_TIMESTAMPS => false,
self::CONFIG_COLUMN_SCHEMA => array( self::CONFIG_COLUMN_SCHEMA => array(
'id' => null, 'id' => null,
@ -42,13 +40,6 @@ final class PhabricatorRepositorySymbol extends PhabricatorRepositoryDAO {
} }
public function getURI() { public function getURI() {
if (!$this->repository) {
// This symbol is in the index, but we don't know which Repository it's
// part of. Usually this means the Arcanist Project hasn't been linked
// to a Repository. We can't generate a URI, so just fail.
return null;
}
$request = DiffusionRequest::newFromDictionary( $request = DiffusionRequest::newFromDictionary(
array( array(
'user' => PhabricatorUser::getOmnipotentUser(), 'user' => PhabricatorUser::getOmnipotentUser(),
@ -75,18 +66,9 @@ final class PhabricatorRepositorySymbol extends PhabricatorRepositoryDAO {
return $this->assertAttached($this->repository); return $this->assertAttached($this->repository);
} }
public function attachRepository($repository) { public function attachRepository(PhabricatorRepository $repository) {
$this->repository = $repository; $this->repository = $repository;
return $this; return $this;
} }
public function getArcanistProject() {
return $this->assertAttached($this->arcanistProject);
}
public function attachArcanistProject($project) {
$this->arcanistProject = $project;
return $this;
}
} }