Improve UX on not found namespace/command

This commit is contained in:
Jordi Boggiano 2016-12-11 14:18:07 +01:00
parent 51bc35cc84
commit aae5fb15ec
3 changed files with 52 additions and 10 deletions

View File

@ -30,6 +30,7 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\ListCommand;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
@ -503,7 +504,7 @@ class Application
$exact = in_array($namespace, $namespaces, true);
if (count($namespaces) > 1 && !$exact) {
throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
throw new CommandNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
}
return $exact ? $namespace : reset($namespaces);
@ -559,9 +560,20 @@ class Application
$exact = in_array($name, $commands, true);
if (count($commands) > 1 && !$exact) {
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
$usableWidth = $this->terminal->getWidth() - 10;
$abbrevs = array_values($commands);
$maxLen = 0;
foreach ($abbrevs as $abbrev) {
$maxLen = max(Helper::strlen($abbrev), $maxLen);
}
$abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen) {
$abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();
throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands));
return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
}, array_values($commands));
$suggestions = $this->getAbbreviationSuggestions($abbrevs);
throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $name, $suggestions), array_values($commands));
}
return $this->get($exact ? $name : reset($commands));
@ -944,7 +956,7 @@ class Application
*/
private function getAbbreviationSuggestions($abbrevs)
{
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
return ' '.implode("\n ", $abbrevs);
}
/**

View File

@ -58,6 +58,24 @@ abstract class Helper implements HelperInterface
return mb_strwidth($string, $encoding);
}
/**
* Returns the subset of a string, using mb_substr if it is available.
*
* @param string $string String to subset
* @param int $from Start offset
* @param int|null $length Length to read
*
* @return string The string subset
*/
public static function substr($string, $from, $length = null)
{
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return substr($string);
}
return mb_substr($string, $from, $length, $encoding);
}
public static function formatTime($secs)
{
static $timeFormats = array(

View File

@ -28,6 +28,7 @@ use Symfony\Component\Console\Tester\ApplicationTester;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\EventDispatcher\EventDispatcher;
class ApplicationTest extends \PHPUnit_Framework_TestCase
@ -211,16 +212,15 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces');
}
/**
* @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
* @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1).
*/
public function testFindAmbiguousNamespace()
{
$application = new Application();
$application->add(new \BarBucCommand());
$application->add(new \FooCommand());
$application->add(new \Foo2Command());
$expectedMsg = "The namespace \"f\" is ambiguous.\nDid you mean one of these?\n foo\n foo1";
$this->setExpectedException(CommandNotFoundException::class, $expectedMsg);
$application->findNamespace('f');
}
@ -279,8 +279,20 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
{
return array(
array('f', 'Command "f" is not defined.'),
array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'),
array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'),
array(
'a',
"Command \"a\" is ambiguous.\nDid you mean one of these?\n".
" afoobar The foo:bar command\n".
" afoobar1 The foo:bar1 command\n".
' afoobar2 The foo1:bar command',
),
array(
'foo:b',
"Command \"foo:b\" is ambiguous.\nDid you mean one of these?\n".
" foo:bar The foo:bar command\n".
" foo:bar1 The foo:bar1 command\n".
' foo1:bar The foo1:bar command',
),
);
}