Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC][Console] Added console style guide helpers (v2) #14057

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0c653a0
added console style guide helpers
kbond Sep 25, 2014
b2bd430
added FormatterInterface and default formatters
kbond Sep 25, 2014
c71222f
added warning result bar
kbond Sep 25, 2014
e20283f
added OutputDecorator
kbond Sep 25, 2014
797a485
added comments
kbond Sep 25, 2014
c1c49c8
adjusted some formats
kbond Sep 25, 2014
bdb3704
added newline helper
kbond Sep 25, 2014
7110316
FormatterInterface::format returns string only
kbond Sep 25, 2014
4b30f14
fix cs
kbond Sep 25, 2014
a358431
fix string padding
kbond Sep 25, 2014
99ebf0e
refactored and simplified
kbond Sep 26, 2014
4d3e078
rename subtitle to section, fix docblocks
kbond Oct 2, 2014
72708d7
refactored
kbond Oct 2, 2014
a59fe02
added table helper
kbond Oct 2, 2014
bafefd6
added question helpers
kbond Oct 2, 2014
d767d28
added style guide table style
kbond Oct 2, 2014
273c187
added StandardQuestionHelper
kbond Oct 2, 2014
b0d2e6a
added table style to Table defaults
kbond Oct 2, 2014
9d82d6a
added `askHidden` helper
kbond Oct 2, 2014
525be2b
fix cs
kbond Oct 2, 2014
16ad7b9
refactored, removed FormatterInterface
kbond Oct 3, 2014
de14472
match current style guide
kbond Oct 3, 2014
d4d6f52
added vertical padding for blocks
kbond Oct 3, 2014
a33d8a7
various fixes
kbond Oct 3, 2014
18dbdb3
revert changes to FormatterHelper
kbond Mar 25, 2015
ae6d7a7
ensure formatter helper exists in QuestionHelper::writeError()
kbond Mar 26, 2015
5c774cc
simplify style tag
kbond Mar 26, 2015
c36189a
rename StyleInterface::ln() to StyleInterface:newLine()
kbond Mar 26, 2015
85cc04c
add StyleInterface::progress()
kbond Mar 26, 2015
e0efaa8
fix bug
kbond Mar 26, 2015
91941f8
add OutputStyle::progressStart, progressAdvance, progressFinish
kbond Mar 26, 2015
8143e77
remove StyleInterface::multipleChoice()
kbond Mar 26, 2015
f0cb90a
set custom progressbar characters only in non-windows environments
kbond Mar 27, 2015
bbdb37c
use DIRECTORY_SEPARATOR to detect Windows
kbond Mar 27, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 47 additions & 24 deletions src/Symfony/Component/Console/Helper/QuestionHelper.php
Expand Up @@ -109,25 +109,11 @@ public function getName()
*/
public function doAsk(OutputInterface $output, Question $question)
{
$inputStream = $this->inputStream ?: STDIN;

$message = $question->getQuestion();
if ($question instanceof ChoiceQuestion) {
$width = max(array_map('strlen', array_keys($question->getChoices())));

$messages = (array) $question->getQuestion();
foreach ($question->getChoices() as $key => $value) {
$messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
}

$output->writeln($messages);

$message = $question->getPrompt();
}

$output->write($message);
$this->writePrompt($output, $question);

$inputStream = $this->inputStream ?: STDIN;
$autocomplete = $question->getAutocompleterValues();

if (null === $autocomplete || !$this->hasSttyAvailable()) {
$ret = false;
if ($question->isHidden()) {
Expand Down Expand Up @@ -160,6 +146,49 @@ public function doAsk(OutputInterface $output, Question $question)
return $ret;
}

/**
* Outputs the question prompt.
*
* @param OutputInterface $output
* @param Question $question
*/
protected function writePrompt(OutputInterface $output, Question $question)
{
$message = $question->getQuestion();

if ($question instanceof ChoiceQuestion) {
$width = max(array_map('strlen', array_keys($question->getChoices())));

$messages = (array) $question->getQuestion();
foreach ($question->getChoices() as $key => $value) {
$messages[] = sprintf(" [<info>%-${width}s</info>] %s", $key, $value);
}

$output->writeln($messages);

$message = $question->getPrompt();
}

$output->write($message);
}

/**
* Outputs an error message.
*
* @param OutputInterface $output
* @param \Exception $error
*/
protected function writeError(OutputInterface $output, \Exception $error)
{
if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
$message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
} else {
$message = '<error>'.$error->getMessage().'</error>';
}

$output->writeln($message);
}

/**
* Autocompletes a question.
*
Expand Down Expand Up @@ -355,13 +384,7 @@ private function validateAttempts($interviewer, OutputInterface $output, Questio
$attempts = $question->getMaxAttempts();
while (null === $attempts || $attempts--) {
if (null !== $error) {
if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
$message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
} else {
$message = '<error>'.$error->getMessage().'</error>';
}

$output->writeln($message);
$this->writeError($output, $error);
}

try {
Expand Down
106 changes: 106 additions & 0 deletions src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php
@@ -0,0 +1,106 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Console\Helper;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
* Symfony Style Guide compliant question helper.
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
class SymfonyQuestionHelper extends QuestionHelper
{
/**
* {@inheritdoc}
*/
public function ask(InputInterface $input, OutputInterface $output, Question $question)
{
$validator = $question->getValidator();
$question->setValidator(function ($value) use ($validator) {
if (null !== $validator && is_callable($validator)) {
$value = $validator($value);
}

// make required
if (!is_array($value) && !is_bool($value) && 0 === strlen($value)) {
throw new \Exception('A value is required.');
}

return $value;
});

return parent::ask($input, $output, $question);
}

/**
* {@inheritdoc}
*/
protected function writePrompt(OutputInterface $output, Question $question)
{
$text = $question->getQuestion();
$default = $question->getDefault();

switch (true) {
case null === $default:
$text = sprintf(' <info>%s</info>:', $text);

break;

case $question instanceof ConfirmationQuestion:
$text = sprintf(' <info>%s (yes/no)</info> [<comment>%s</comment>]:', $text, $default ? 'yes' : 'no');

break;

case $question instanceof ChoiceQuestion:
$choices = $question->getChoices();
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $choices[$default]);

break;

default:
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $default);
}

$output->writeln($text);

if ($question instanceof ChoiceQuestion) {
$width = max(array_map('strlen', array_keys($question->getChoices())));

foreach ($question->getChoices() as $key => $value) {
$output->writeln(sprintf(" [<comment>%-${width}s</comment>] %s", $key, $value));
}
}

$output->write(' > ');
}

/**
* {@inheritdoc}
*/
protected function writeError(OutputInterface $output, \Exception $error)
{
if ($output instanceof SymfonyStyle) {
$output->newLine();
$output->error($error->getMessage());

return;
}

parent::writeError($output, $error);
}
}
9 changes: 9 additions & 0 deletions src/Symfony/Component/Console/Helper/Table.php
Expand Up @@ -401,10 +401,19 @@ private static function initStyles()
->setCellRowContentFormat('%s')
;

$styleGuide = new TableStyle();
$styleGuide
->setHorizontalBorderChar('-')
->setVerticalBorderChar(' ')
->setCrossingChar(' ')
->setCellHeaderFormat('%s')
;

return array(
'default' => new TableStyle(),
'borderless' => $borderless,
'compact' => $compact,
'symfony-style-guide' => $styleGuide,
);
}
}
116 changes: 116 additions & 0 deletions src/Symfony/Component/Console/Style/OutputStyle.php
@@ -0,0 +1,116 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Console\Style;

use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Decorates output to add console style guide helpers
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
abstract class OutputStyle implements OutputInterface, StyleInterface
{
private $output;

/**
* @param OutputInterface $output
*/
public function __construct(OutputInterface $output)
{
$this->output = $output;
}

/**
* {@inheritdoc}
*/
public function newLine($count = 1)
{
$this->output->write(str_repeat(PHP_EOL, $count));
}

/**
* @param int $max
*
* @return ProgressBar
*/
public function createProgressBar($max = 0)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this public method isn't in the interface but it could be useful for users to create a custom ProgressBar. Let me know if you want me to change.

{
return new ProgressBar($this->output, $max);
}

/**
* {@inheritdoc}
*/
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
{
$this->output->write($messages, $newline, $type);
}

/**
* {@inheritdoc}
*/
public function writeln($messages, $type = self::OUTPUT_NORMAL)
{
$this->output->writeln($messages, $type);
}

/**
* {@inheritdoc}
*/
public function setVerbosity($level)
{
$this->output->setVerbosity($level);
}

/**
* {@inheritdoc}
*/
public function getVerbosity()
{
return $this->output->getVerbosity();
}

/**
* {@inheritdoc}
*/
public function setDecorated($decorated)
{
$this->output->setDecorated($decorated);
}

/**
* {@inheritdoc}
*/
public function isDecorated()
{
return $this->output->isDecorated();
}

/**
* {@inheritdoc}
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
$this->output->setFormatter($formatter);
}

/**
* {@inheritdoc}
*/
public function getFormatter()
{
return $this->output->getFormatter();
}
}