Improve UX on not found namespace/command
This commit is contained in:
parent
51bc35cc84
commit
aae5fb15ec
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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(
|
||||
|
@ -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',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user