Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 462a02b

Browse files
committed
feature #20869 [Console] Improve UX on not found namespace/command (Seldaek)
This PR was merged into the 3.3-dev branch. Discussion ---------- [Console] Improve UX on not found namespace/command | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT This improves the DX/UX when you don't remember what a command is called.. Traditionally you get this message saying "command x is ambiguous (Y, Z or 6 more)" and if the one you are looking for is in the 6 more you are out of luck. You then have to run the console without arg again, get 50 commands displayed, then have to scroll up to find which one it is you meant. With this patch you get all suggestions always, even with description, so you can make an informed decision right away. See before/after on the screenshot below. ![image](https://cloud.githubusercontent.com/assets/183678/21080350/c3d446ea-bfac-11e6-934b-ba3d7c3dd34d.png) Commits ------- aae5fb1 Improve UX on not found namespace/command
2 parents 795a240 + aae5fb1 commit 462a02b

File tree

3 files changed

+52
-10
lines changed

3 files changed

+52
-10
lines changed

src/Symfony/Component/Console/Application.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use Symfony\Component\Console\Command\HelpCommand;
3131
use Symfony\Component\Console\Command\ListCommand;
3232
use Symfony\Component\Console\Helper\HelperSet;
33+
use Symfony\Component\Console\Helper\Helper;
3334
use Symfony\Component\Console\Helper\FormatterHelper;
3435
use Symfony\Component\Console\Event\ConsoleCommandEvent;
3536
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
@@ -503,7 +504,7 @@ public function findNamespace($namespace)
503504

504505
$exact = in_array($namespace, $namespaces, true);
505506
if (count($namespaces) > 1 && !$exact) {
506-
throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
507+
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));
507508
}
508509

509510
return $exact ? $namespace : reset($namespaces);
@@ -559,9 +560,20 @@ public function find($name)
559560

560561
$exact = in_array($name, $commands, true);
561562
if (count($commands) > 1 && !$exact) {
562-
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
563+
$usableWidth = $this->terminal->getWidth() - 10;
564+
$abbrevs = array_values($commands);
565+
$maxLen = 0;
566+
foreach ($abbrevs as $abbrev) {
567+
$maxLen = max(Helper::strlen($abbrev), $maxLen);
568+
}
569+
$abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen) {
570+
$abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();
571+
572+
return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
573+
}, array_values($commands));
574+
$suggestions = $this->getAbbreviationSuggestions($abbrevs);
563575

564-
throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands));
576+
throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $name, $suggestions), array_values($commands));
565577
}
566578

567579
return $this->get($exact ? $name : reset($commands));
@@ -944,7 +956,7 @@ protected function getDefaultHelperSet()
944956
*/
945957
private function getAbbreviationSuggestions($abbrevs)
946958
{
947-
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
959+
return ' '.implode("\n ", $abbrevs);
948960
}
949961

950962
/**

src/Symfony/Component/Console/Helper/Helper.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ public static function strlen($string)
5858
return mb_strwidth($string, $encoding);
5959
}
6060

61+
/**
62+
* Returns the subset of a string, using mb_substr if it is available.
63+
*
64+
* @param string $string String to subset
65+
* @param int $from Start offset
66+
* @param int|null $length Length to read
67+
*
68+
* @return string The string subset
69+
*/
70+
public static function substr($string, $from, $length = null)
71+
{
72+
if (false === $encoding = mb_detect_encoding($string, null, true)) {
73+
return substr($string);
74+
}
75+
76+
return mb_substr($string, $from, $length, $encoding);
77+
}
78+
6179
public static function formatTime($secs)
6280
{
6381
static $timeFormats = array(

src/Symfony/Component/Console/Tests/ApplicationTest.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use Symfony\Component\Console\Event\ConsoleCommandEvent;
2929
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
3030
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
31+
use Symfony\Component\Console\Exception\CommandNotFoundException;
3132
use Symfony\Component\EventDispatcher\EventDispatcher;
3233

3334
class ApplicationTest extends \PHPUnit_Framework_TestCase
@@ -211,16 +212,15 @@ public function testFindNamespaceWithSubnamespaces()
211212
$this->assertEquals('foo', $application->findNamespace('foo'), '->findNamespace() returns commands even if the commands are only contained in subnamespaces');
212213
}
213214

214-
/**
215-
* @expectedException \Symfony\Component\Console\Exception\CommandNotFoundException
216-
* @expectedExceptionMessage The namespace "f" is ambiguous (foo, foo1).
217-
*/
218215
public function testFindAmbiguousNamespace()
219216
{
220217
$application = new Application();
221218
$application->add(new \BarBucCommand());
222219
$application->add(new \FooCommand());
223220
$application->add(new \Foo2Command());
221+
222+
$expectedMsg = "The namespace \"f\" is ambiguous.\nDid you mean one of these?\n foo\n foo1";
223+
$this->setExpectedException(CommandNotFoundException::class, $expectedMsg);
224224
$application->findNamespace('f');
225225
}
226226

@@ -279,8 +279,20 @@ public function provideAmbiguousAbbreviations()
279279
{
280280
return array(
281281
array('f', 'Command "f" is not defined.'),
282-
array('a', 'Command "a" is ambiguous (afoobar, afoobar1 and 1 more).'),
283-
array('foo:b', 'Command "foo:b" is ambiguous (foo:bar, foo:bar1 and 1 more).'),
282+
array(
283+
'a',
284+
"Command \"a\" is ambiguous.\nDid you mean one of these?\n".
285+
" afoobar The foo:bar command\n".
286+
" afoobar1 The foo:bar1 command\n".
287+
' afoobar2 The foo1:bar command',
288+
),
289+
array(
290+
'foo:b',
291+
"Command \"foo:b\" is ambiguous.\nDid you mean one of these?\n".
292+
" foo:bar The foo:bar command\n".
293+
" foo:bar1 The foo:bar1 command\n".
294+
' foo1:bar The foo1:bar command',
295+
),
284296
);
285297
}
286298

0 commit comments

Comments
 (0)