diff --git a/Application.php b/Application.php index 9d5eedb68..b01effffa 100644 --- a/Application.php +++ b/Application.php @@ -986,10 +986,6 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI }); } } - - foreach ($commandSignals as $signal) { - $this->signalRegistry->register($signal, [$command, 'handleSignal']); - } } if (null !== $this->dispatcher) { @@ -1008,6 +1004,10 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI }); } } + + foreach ($commandSignals as $signal) { + $this->signalRegistry->register($signal, [$command, 'handleSignal']); + } } if (null === $this->dispatcher) { diff --git a/Command/DumpCompletionCommand.php b/Command/DumpCompletionCommand.php index dc0cfaef7..518d606a0 100644 --- a/Command/DumpCompletionCommand.php +++ b/Command/DumpCompletionCommand.php @@ -93,8 +93,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (!file_exists($completionFile)) { $supportedShells = $this->getSupportedShells(); - ($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output) - ->writeln(sprintf('Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").', $shell, implode('", "', $supportedShells))); + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + if ($shell) { + $output->writeln(sprintf('Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").', $shell, implode('", "', $supportedShells))); + } else { + $output->writeln(sprintf('Shell not detected, Symfony shell completion only supports "%s").', implode('", "', $supportedShells))); + } return self::INVALID; } diff --git a/Exception/InvalidOptionException.php b/Exception/InvalidOptionException.php index b2eec6165..5cf62792e 100644 --- a/Exception/InvalidOptionException.php +++ b/Exception/InvalidOptionException.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Console\Exception; /** - * Represents an incorrect option name typed in the console. + * Represents an incorrect option name or value typed in the console. * * @author Jérôme Tamarelle */ diff --git a/Helper/ProgressBar.php b/Helper/ProgressBar.php index 72c26f2f3..93b15279a 100644 --- a/Helper/ProgressBar.php +++ b/Helper/ProgressBar.php @@ -466,10 +466,13 @@ private function overwrite(string $message): void } $this->output->clear($lineCount); } else { - for ($i = 0; $i < $this->formatLineCount; ++$i) { - $this->cursor->moveToColumn(1); - $this->cursor->clearLine(); - $this->cursor->moveUp(); + if ('' !== $this->previousMessage) { + // only clear upper lines when last call was not a clear + for ($i = 0; $i < $this->formatLineCount; ++$i) { + $this->cursor->moveToColumn(1); + $this->cursor->clearLine(); + $this->cursor->moveUp(); + } } $this->cursor->moveToColumn(1); diff --git a/Resources/completion.bash b/Resources/completion.bash index bf3edf511..64b87ccf7 100644 --- a/Resources/completion.bash +++ b/Resources/completion.bash @@ -11,13 +11,14 @@ _sf_{{ COMMAND_NAME }}() { local sf_cmd="${COMP_WORDS[0]}" # for an alias, get the real script behind it - if [[ $(type -t $sf_cmd) == "alias" ]]; then + sf_cmd_type=$(type -t $sf_cmd) + if [[ $sf_cmd_type == "alias" ]]; then sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/") - else + elif [[ $sf_cmd_type == "file" ]]; then sf_cmd=$(type -p $sf_cmd) fi - if [ ! -x "$sf_cmd" ]; then + if [[ $sf_cmd_type != "function" && ! -x $sf_cmd ]]; then return 1 fi diff --git a/Tests/ApplicationTest.php b/Tests/ApplicationTest.php index 70e54870b..ccfdf82ff 100644 --- a/Tests/ApplicationTest.php +++ b/Tests/ApplicationTest.php @@ -1945,6 +1945,10 @@ public function testSignalSubscriber() */ public function testSetSignalsToDispatchEvent() { + if (!\defined('SIGUSR1')) { + $this->markTestSkipped('SIGUSR1 not available'); + } + $command = new BaseSignableCommand(); $subscriber = new SignalEventSubscriber(); @@ -1975,6 +1979,25 @@ public function testSignalableCommandInterfaceWithoutSignals() $this->assertSame(0, $application->run(new ArrayInput(['signal']))); } + public function testSignalableCommandHandlerCalledAfterEventListener() + { + if (!\defined('SIGUSR1')) { + $this->markTestSkipped('SIGUSR1 not available'); + } + + $command = new SignableCommand(); + + $subscriber = new SignalEventSubscriber(); + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber($subscriber); + + $application = $this->createSignalableApplication($command, $dispatcher); + $application->setSignalsToDispatchEvent(\SIGUSR1); + $this->assertSame(1, $application->run(new ArrayInput(['signal']))); + $this->assertSame([SignalEventSubscriber::class, SignableCommand::class], $command->signalHandlers); + } + /** * @group tty */ @@ -2073,6 +2096,7 @@ public function isEnabled(): bool class BaseSignableCommand extends Command { public $signaled = false; + public $signalHandlers = []; public $loop = 1000; private $emitsSignal; @@ -2113,6 +2137,7 @@ public function getSubscribedSignals(): array public function handleSignal(int $signal): void { $this->signaled = true; + $this->signalHandlers[] = __CLASS__; } } @@ -2124,6 +2149,7 @@ public function onSignal(ConsoleSignalEvent $event): void { $this->signaled = true; $event->getCommand()->signaled = true; + $event->getCommand()->signalHandlers[] = __CLASS__; } public static function getSubscribedEvents(): array diff --git a/Tests/Helper/ProgressBarTest.php b/Tests/Helper/ProgressBarTest.php index ef5f06222..17401b887 100644 --- a/Tests/Helper/ProgressBarTest.php +++ b/Tests/Helper/ProgressBarTest.php @@ -812,8 +812,10 @@ public function testMultilineFormat() $this->assertEquals( ">---------------------------\nfoobar". $this->generateOutput("=========>------------------\nfoobar"). - "\x1B[1G\x1B[2K\x1B[1A\x1B[1G\x1B[2K". - $this->generateOutput("============================\nfoobar"), + "\x1B[1G\x1B[2K\x1B[1A". + $this->generateOutput(''). + $this->generateOutput('============================'). + "\nfoobar", stream_get_contents($output->getStream()) ); } @@ -1124,4 +1126,29 @@ public function testMultiLineFormatIsFullyCleared() stream_get_contents($output->getStream()) ); } + + public function testMultiLineFormatIsFullyCorrectlyWithManuallyCleanup() + { + ProgressBar::setFormatDefinition('normal_nomax', "[%bar%]\n%message%"); + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setMessage('Processing "foobar"...'); + $bar->start(); + $bar->clear(); + $output->writeln('Foo!'); + $bar->display(); + $bar->finish(); + + rewind($output->getStream()); + $this->assertEquals( + "[>---------------------------]\n". + 'Processing "foobar"...'. + "\x1B[1G\x1B[2K\x1B[1A". + $this->generateOutput(''). + 'Foo!'.\PHP_EOL. + $this->generateOutput('[--->------------------------]'). + "\nProcessing \"foobar\"...". + $this->generateOutput("[----->----------------------]\nProcessing \"foobar\"..."), + stream_get_contents($output->getStream()) + ); + } }