mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-22 23:02:41 +01:00
Remove command spelling correction from Arcanist
Summary: Fixes T7489. Depends on D16332, which moved this code to libphutil. Test Plan: ``` $ arc banch --bystatus (Assuming 'banch' is the British spelling of 'branch'.) (Assuming '--bystatus' is the British spelling of '--by-status'.) ... ``` Reviewers: chad Reviewed By: chad Maniphest Tasks: T7489 Differential Revision: https://secure.phabricator.com/D16333
This commit is contained in:
parent
1bedfdccd7
commit
06c641f92c
4 changed files with 4 additions and 177 deletions
|
@ -41,7 +41,6 @@ phutil_register_library_map(array(
|
||||||
'ArcanistBraceFormattingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistBraceFormattingXHPASTLinterRule.php',
|
'ArcanistBraceFormattingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistBraceFormattingXHPASTLinterRule.php',
|
||||||
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBraceFormattingXHPASTLinterRuleTestCase.php',
|
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistBraceFormattingXHPASTLinterRuleTestCase.php',
|
||||||
'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php',
|
'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php',
|
||||||
'ArcanistBritishTestCase' => 'configuration/__tests__/ArcanistBritishTestCase.php',
|
|
||||||
'ArcanistBrowseWorkflow' => 'workflow/ArcanistBrowseWorkflow.php',
|
'ArcanistBrowseWorkflow' => 'workflow/ArcanistBrowseWorkflow.php',
|
||||||
'ArcanistBundle' => 'parser/ArcanistBundle.php',
|
'ArcanistBundle' => 'parser/ArcanistBundle.php',
|
||||||
'ArcanistBundleTestCase' => 'parser/__tests__/ArcanistBundleTestCase.php',
|
'ArcanistBundleTestCase' => 'parser/__tests__/ArcanistBundleTestCase.php',
|
||||||
|
@ -456,7 +455,6 @@ phutil_register_library_map(array(
|
||||||
'ArcanistBraceFormattingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistBraceFormattingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistBraceFormattingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow',
|
'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow',
|
||||||
'ArcanistBritishTestCase' => 'PhutilTestCase',
|
|
||||||
'ArcanistBrowseWorkflow' => 'ArcanistWorkflow',
|
'ArcanistBrowseWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistBundle' => 'Phobject',
|
'ArcanistBundle' => 'Phobject',
|
||||||
'ArcanistBundleTestCase' => 'PhutilTestCase',
|
'ArcanistBundleTestCase' => 'PhutilTestCase',
|
||||||
|
|
|
@ -144,7 +144,8 @@ class ArcanistConfiguration extends Phobject {
|
||||||
|
|
||||||
// We haven't found a real command, alias, or unique prefix. Try similar
|
// We haven't found a real command, alias, or unique prefix. Try similar
|
||||||
// spellings.
|
// spellings.
|
||||||
$corrected = self::correctCommandSpelling($command, $all, 2);
|
$corrected = PhutilArgumentSpellingCorrector::newCommandCorrector()
|
||||||
|
->correctSpelling($command, $all);
|
||||||
if (count($corrected) == 1) {
|
if (count($corrected) == 1) {
|
||||||
$console->writeErr(
|
$console->writeErr(
|
||||||
pht(
|
pht(
|
||||||
|
@ -183,78 +184,4 @@ class ArcanistConfiguration extends Phobject {
|
||||||
return array_keys($is_prefix);
|
return array_keys($is_prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function correctCommandSpelling(
|
|
||||||
$command,
|
|
||||||
array $options,
|
|
||||||
$max_distance) {
|
|
||||||
|
|
||||||
// Adjust to the scaled edit costs we use below, so "2" roughly means
|
|
||||||
// "2 edits".
|
|
||||||
$max_distance = $max_distance * 3;
|
|
||||||
|
|
||||||
// These costs are somewhat made up, but the theory is that it is far more
|
|
||||||
// likely you will mis-strike a key ("lans" for "land") or press two keys
|
|
||||||
// out of order ("alnd" for "land") than omit keys or press extra keys.
|
|
||||||
$matrix = id(new PhutilEditDistanceMatrix())
|
|
||||||
->setInsertCost(4)
|
|
||||||
->setDeleteCost(4)
|
|
||||||
->setReplaceCost(3)
|
|
||||||
->setTransposeCost(2);
|
|
||||||
|
|
||||||
return self::correctSpelling($command, $options, $matrix, $max_distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function correctArgumentSpelling($command, array $options) {
|
|
||||||
$max_distance = 1;
|
|
||||||
|
|
||||||
// We are stricter with arguments - we allow only one inserted or deleted
|
|
||||||
// character. It is mainly to handle cases like --no-lint versus --nolint
|
|
||||||
// or --reviewer versus --reviewers.
|
|
||||||
$matrix = id(new PhutilEditDistanceMatrix())
|
|
||||||
->setInsertCost(1)
|
|
||||||
->setDeleteCost(1)
|
|
||||||
->setReplaceCost(10);
|
|
||||||
|
|
||||||
return self::correctSpelling($command, $options, $matrix, $max_distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function correctSpelling(
|
|
||||||
$input,
|
|
||||||
array $options,
|
|
||||||
PhutilEditDistanceMatrix $matrix,
|
|
||||||
$max_distance) {
|
|
||||||
|
|
||||||
$distances = array();
|
|
||||||
$inputv = str_split($input);
|
|
||||||
foreach ($options as $option) {
|
|
||||||
$optionv = str_split($option);
|
|
||||||
$matrix->setSequences($optionv, $inputv);
|
|
||||||
$distances[$option] = $matrix->getEditDistance();
|
|
||||||
}
|
|
||||||
|
|
||||||
asort($distances);
|
|
||||||
$best = min($max_distance, reset($distances));
|
|
||||||
foreach ($distances as $option => $distance) {
|
|
||||||
if ($distance > $best) {
|
|
||||||
unset($distances[$option]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before filtering, check if we have multiple equidistant matches and
|
|
||||||
// return them if we do. This prevents us from, e.g., matching "alnd" with
|
|
||||||
// both "land" and "amend", then dropping "land" for being too short, and
|
|
||||||
// incorrectly completing to "amend".
|
|
||||||
if (count($distances) > 1) {
|
|
||||||
return array_keys($distances);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($distances as $option => $distance) {
|
|
||||||
if (strlen($option) < $distance) {
|
|
||||||
unset($distances[$option]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_keys($distances);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
final class ArcanistBritishTestCase extends PhutilTestCase {
|
|
||||||
|
|
||||||
public function testCommandCompletion() {
|
|
||||||
$this->assertCommandCompletion(
|
|
||||||
array('land'),
|
|
||||||
'alnd',
|
|
||||||
array('land', 'amend'));
|
|
||||||
|
|
||||||
$this->assertCommandCompletion(
|
|
||||||
array('branch'),
|
|
||||||
'brnach',
|
|
||||||
array('branch', 'browse'));
|
|
||||||
|
|
||||||
$this->assertCommandCompletion(
|
|
||||||
array(),
|
|
||||||
'test',
|
|
||||||
array('list', 'unit'));
|
|
||||||
|
|
||||||
$this->assertCommandCompletion(
|
|
||||||
array('list'),
|
|
||||||
'lists',
|
|
||||||
array('list'));
|
|
||||||
|
|
||||||
$this->assertCommandCompletion(
|
|
||||||
array('diff'),
|
|
||||||
'dfif',
|
|
||||||
array('diff'));
|
|
||||||
|
|
||||||
$this->assertCommandCompletion(
|
|
||||||
array('unit'),
|
|
||||||
'uint',
|
|
||||||
array('unit', 'lint', 'list'));
|
|
||||||
|
|
||||||
$this->assertCommandCompletion(
|
|
||||||
array('list', 'lint'),
|
|
||||||
'nilt',
|
|
||||||
array('unit', 'lint', 'list'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertCommandCompletion($expect, $input, $commands) {
|
|
||||||
$result = ArcanistConfiguration::correctCommandSpelling(
|
|
||||||
$input,
|
|
||||||
$commands,
|
|
||||||
2);
|
|
||||||
|
|
||||||
sort($result);
|
|
||||||
sort($expect);
|
|
||||||
|
|
||||||
$commands = implode(', ', $commands);
|
|
||||||
|
|
||||||
$this->assertEqual(
|
|
||||||
$expect,
|
|
||||||
$result,
|
|
||||||
pht('Correction of %s against: %s', $input, $commands));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testArgumentCompletion() {
|
|
||||||
$this->assertArgumentCompletion(
|
|
||||||
array('nolint'),
|
|
||||||
'no-lint',
|
|
||||||
array('nolint', 'nounit'));
|
|
||||||
|
|
||||||
$this->assertArgumentCompletion(
|
|
||||||
array('reviewers'),
|
|
||||||
'reviewer',
|
|
||||||
array('reviewers', 'cc'));
|
|
||||||
|
|
||||||
$this->assertArgumentCompletion(
|
|
||||||
array(),
|
|
||||||
'onlint',
|
|
||||||
array('nolint'));
|
|
||||||
|
|
||||||
$this->assertArgumentCompletion(
|
|
||||||
array(),
|
|
||||||
'nolind',
|
|
||||||
array('nolint'));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function assertArgumentCompletion($expect, $input, $arguments) {
|
|
||||||
$result = ArcanistConfiguration::correctArgumentSpelling(
|
|
||||||
$input,
|
|
||||||
$arguments);
|
|
||||||
|
|
||||||
sort($result);
|
|
||||||
sort($expect);
|
|
||||||
|
|
||||||
$arguments = implode(', ', $arguments);
|
|
||||||
|
|
||||||
$this->assertEqual(
|
|
||||||
$expect,
|
|
||||||
$result,
|
|
||||||
pht('Correction of %s against: %s', $input, $arguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -673,9 +673,8 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!array_key_exists($arg_key, $spec)) {
|
if (!array_key_exists($arg_key, $spec)) {
|
||||||
$corrected = ArcanistConfiguration::correctArgumentSpelling(
|
$corrected = PhutilArgumentSpellingCorrector::newFlagCorrector()
|
||||||
$arg_key,
|
->correctSpelling($arg_key, array_keys($spec));
|
||||||
array_keys($spec));
|
|
||||||
if (count($corrected) == 1) {
|
if (count($corrected) == 1) {
|
||||||
PhutilConsole::getConsole()->writeErr(
|
PhutilConsole::getConsole()->writeErr(
|
||||||
pht(
|
pht(
|
||||||
|
|
Loading…
Reference in a new issue