Skip to content

Commit f91c965

Browse files
committed
feature #47750 [Console] Show available commands in namespace when running namespace as command (wouterj)
This PR was merged into the 6.2 branch. Discussion ---------- [Console] Show available commands in namespace when running namespace as command | Q | A | ------------- | --- | Branch? | 6.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | n/a Another little UX improvement found in the Docker CLI: When running the namespace as a command (e.g. `bin/console make`), instead of showing an error "this command does not exist", show the list of sub-commands in that namespace. I've kept the exit code and dispatching of the error event in place, to avoid any BC breaks on this matter. **Before** ``` $ bin/console debug Command "debug" is not defined. Did you mean one of these? debug:autowiring debug:config debug:container debug:dotenv debug:event-dispatcher debug:router ``` **After** ``` $ bin/console debug Symfony 6.2.0-DEV (env: dev, debug: true) #StandWithUkraine https://sf.to/ukraine Usage: command [options] [arguments] Options: -h, --help Display help for the given command. When no command is given display help for the list command -q, --quiet Do not output any message -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question -e, --env=ENV The Environment name. [default: "dev"] --no-debug Switch off debug mode. -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands for the "debug" namespace: debug:autowiring List classes/interfaces you can use for autowiring debug:config Dump the current configuration for an extension debug:container Display current services for an application debug:dotenv Lists all dotenv files with variables and values debug:event-dispatcher Display configured listeners for an application debug:router Display current routes for an application ``` Commits ------- 912ecd8 Show available commands in namespace when running namespace as command
2 parents 053613c + 912ecd8 commit f91c965

File tree

2 files changed

+49
-18
lines changed

2 files changed

+49
-18
lines changed

src/Symfony/Component/Console/Application.php

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Symfony\Component\Console\Exception\RuntimeException;
3434
use Symfony\Component\Console\Formatter\OutputFormatter;
3535
use Symfony\Component\Console\Helper\DebugFormatterHelper;
36+
use Symfony\Component\Console\Helper\DescriptorHelper;
3637
use Symfony\Component\Console\Helper\FormatterHelper;
3738
use Symfony\Component\Console\Helper\Helper;
3839
use Symfony\Component\Console\Helper\HelperSet;
@@ -259,7 +260,24 @@ public function doRun(InputInterface $input, OutputInterface $output)
259260
// the command name MUST be the first element of the input
260261
$command = $this->find($name);
261262
} catch (\Throwable $e) {
262-
if (!($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) || 1 !== \count($alternatives = $e->getAlternatives()) || !$input->isInteractive()) {
263+
if (($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) && 1 === \count($alternatives = $e->getAlternatives()) && $input->isInteractive()) {
264+
$alternative = $alternatives[0];
265+
266+
$style = new SymfonyStyle($input, $output);
267+
$style->block(sprintf("\nCommand \"%s\" is not defined.\n", $name), null, 'error');
268+
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
269+
if (null !== $this->dispatcher) {
270+
$event = new ConsoleErrorEvent($input, $output, $e);
271+
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
272+
273+
return $event->getExitCode();
274+
}
275+
276+
return 1;
277+
}
278+
279+
$command = $this->find($alternative);
280+
} else {
263281
if (null !== $this->dispatcher) {
264282
$event = new ConsoleErrorEvent($input, $output, $e);
265283
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
@@ -271,25 +289,22 @@ public function doRun(InputInterface $input, OutputInterface $output)
271289
$e = $event->getError();
272290
}
273291

274-
throw $e;
275-
}
276-
277-
$alternative = $alternatives[0];
278-
279-
$style = new SymfonyStyle($input, $output);
280-
$style->block(sprintf("\nCommand \"%s\" is not defined.\n", $name), null, 'error');
281-
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
282-
if (null !== $this->dispatcher) {
283-
$event = new ConsoleErrorEvent($input, $output, $e);
284-
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
285-
286-
return $event->getExitCode();
292+
try {
293+
if ($e instanceof CommandNotFoundException && $namespace = $this->findNamespace($name)) {
294+
$helper = new DescriptorHelper();
295+
$helper->describe($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output, $this, [
296+
'format' => 'txt',
297+
'raw_text' => false,
298+
'namespace' => $namespace,
299+
'short' => false,
300+
]);
301+
302+
return isset($event) ? $event->getExitCode() : 1;
303+
}
304+
} catch (NamespaceNotFoundException) {
305+
throw $e;
287306
}
288-
289-
return 1;
290307
}
291-
292-
$command = $this->find($alternative);
293308
}
294309

295310
if ($command instanceof LazyCommand) {

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,22 @@ public function provideInvalidCommandNamesSingle()
555555
];
556556
}
557557

558+
public function testRunNamespace()
559+
{
560+
putenv('COLUMNS=120');
561+
$application = new Application();
562+
$application->setAutoExit(false);
563+
$application->add(new \FooCommand());
564+
$application->add(new \Foo1Command());
565+
$application->add(new \Foo2Command());
566+
$tester = new ApplicationTester($application);
567+
$tester->run(['command' => 'foo'], ['decorated' => false]);
568+
$display = trim($tester->getDisplay(true));
569+
$this->assertStringContainsString('Available commands for the "foo" namespace:', $display);
570+
$this->assertStringContainsString('The foo:bar command', $display);
571+
$this->assertStringContainsString('The foo:bar1 command', $display);
572+
}
573+
558574
public function testFindAlternativeExceptionMessageMultiple()
559575
{
560576
putenv('COLUMNS=120');

0 commit comments

Comments
 (0)