-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[RFC][Console] Added console style guide helpers #12035
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
Conversation
protected $messages; | ||
protected $style; | ||
protected $large; | ||
protected $padLength; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these should be private
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need the messages property in a subclass... should I make a getter?
Thanks @kbond for your work!! I'd like to propose you something: as we are talking about DX, the internal source code of these helpers doesn't matter a this moment. What I'd like to see is the real code that developers must write to apply this style guide. The public API must be concise, beautiful, memorable, etc. Otherwise, this initiative will fail. That's why I've prepared a Full Sample Command which contains all the elements of the command output. This is the image of how it would look: This is the full text that should be output to the console:
And what I ask you, if you agree, is that you provide the source code needed to do that. It doesn't matter that the real methods don't exist. I'm not going to execute the code. I just want to see it. |
{ | ||
return array( | ||
sprintf('<fg=blue>%s</fg=blue>', $this->title), | ||
sprintf('<fg=blue>%s</fg=blue>', str_repeat('-', strlen($this->title))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be better to create a <title>
style to make it easier to use it directly (these classes should be optional to make it easier to write bigger messages)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean adding <title>
to OutputFormatter
?
I don't like the fact that you need to instantiate many new objects for each places needing to be formatted. |
@javiereguiluz sure, I can do that - where do you want me to post the code? to this PR? |
@kbond yeah, I think it will be much more usable. I suggest you do the work asked by @javiereguiluz with both implementations, so that it can be compared easily. |
Well the code should be the same for both implementations. The FormatterHelper methods remain the same. I will post the code. |
Their usage in the FormatterHelper is simply a convenience for the default ones to make a clean API. You can still add your own or use the default ones in your code. |
@kbond and given that |
This is awesome! :) |
My suggestion was mainly to allow the following
Just a small example of what it allows people to do: class AuthorFormatter implements FormatterInterface
{
private $entity;
public function __construct(UserEntity $entity)
{
$this->entity = $entity;
}
public function format()
{
return sprintf('%s <info>(%s)</info>', $this->entity->getUsername(), $this->entity->getEmail());
}
} Now lets say I wanted to print this to a file, I can implement my own formatter to remove all the console output to define colors and styles before actually returning it. Currently that's impossible. |
$formatter = new MyFormatter('foo bar');
$output->writeln($formatter->format()); You can do: $output->writeln($formatterHelper->format(new MyFormatter('foo bar'))); |
I wish the final public API doesn't look like that :( I was thinking about something like: // the content is title-cased automatically
$output->title("Lorem ipsum dolor sit amet");
// the content is auto wrapped to the right length
$output->info('Duis aute irure dolor in reprehenderit in voluptate velit esse ... ');
// the [OK] prefix and the padding spaces are automatically added
$output->success('Lorem ipsum dolor sit amet, consectetur adipisicing elit'); |
@iltar if you want to write to a file, just use a non-decorated output, and it will disable all colors without having to change any code in the commands. This even works out of the box if you use @javiereguiluz we cannot add new methods on the OutputInterface, and it would be bad to force each output implementation to reimplement the styleguide formatting (the OutputInterface is responsible of writing to the output, which can be done either with a StreamOutput, a BufferOutput or a NullOutput currently in core) |
For the code to generate @javiereguiluz's requirement, this the what I have so far (missing Questions and Table): protected function execute(InputInterface $input, OutputInterface $output)
{
/** @var FormatterHelper $formatter */
$formatter = $this->getHelper('formatter');
$output->writeln($formatter->formatTitle('Lorem Ipsum Dolor Sit Amet'));
$output->writeln($formatter->formatText(array(
'Duis aute irure dolor in reprehenderit in voluptate velit esse',
'cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat'
)));
$output->writeln('');
// TODO table
$output->writeln($formatter->formatCautionBlock(array(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit,',
'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.'
)));
$output->writeln($formatter->formatSectionTitle('Consectetur Adipisicing Elit Sed Do Eiusmod'));
$output->writeln($formatter->formatListElement(array(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod',
'tempor incididunt ut labore et dolore magna aliqua.'
)));
$output->writeln($formatter->formatListElement(array(
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut',
'aliquip ex ea commodo.'
)));
$output->writeln($formatter->formatListElement(array(
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt',
'mollit anim id est laborum.'
)));
// TODO questions
$output->writeln($formatter->formatNoteBlock(array(
'Duis aute irure dolor in reprehenderit in voluptate velit esse',
'cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat.'
)));
$output->writeln($formatter->formatSuccessResultBar('Lorem ipsum dolor sit amet, consectetur adipisicing elit'));
$output->writeln($formatter->formatErrorResultBar('Duis aute irure dolor in reprehenderit in voluptate velit esse.'));
$output->writeln($formatter->formatWarningResultBar('Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi.'));
} |
@stof sure! I wasn't exactly referring to |
@javiereguiluz What about the ability to create a wrapper around the OutputInterface that has it, without the need of a helper: $output = new OutputDecorator($outputInterface);
// the content is title-cased automatically
$output->title("Lorem ipsum dolor sit amet");
// the content is auto wrapped to the right length
$output->info('Duis aute irure dolor in reprehenderit in voluptate velit esse ... ');
// the [OK] prefix and the padding spaces are automatically added
$output->success('Lorem ipsum dolor sit amet, consectetur adipisicing elit'); edit: this decorator would of course replace the helper |
@kbond thanks a lot for your quick response. I guess you imagine my answer, so I will be pretty sincere: I don't like that API. In fact, as a developer I would like to never use something like that :( What do you think we could do to reduce its verbosity? If this is something that we cannot solve with the existing Console component classes, we could create something new specially crafted for this. What do you think? |
I like the |
We can debate using Formatter classes, but I think the API should be the same whether we use them or not. |
I like the |
Here is the code updated using an OutputDecorator: protected function execute(InputInterface $input, OutputInterface $output)
{
$output = new OutputDecorator($output);
$output->title('Lorem Ipsum Dolor Sit Amet');
$output->text(array(
'Duis aute irure dolor in reprehenderit in voluptate velit esse',
'cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat'
));
$output->writeln('');
// TODO table
$output->caution('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.');
$output->subtitle('Consectetur Adipisicing Elit Sed Do Eiusmod');
$output->listing(array(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod, tempor incididunt ut labore et dolore magna aliqua.',
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.',
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
));
// TODO questions
$output->note(array(
'Duis aute irure dolor in reprehenderit in voluptate velit esse',
'cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat. cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat. (second paragraph)'
));
$output->success('Lorem ipsum dolor sit amet, consectetur adipisicing elit');
$output->error('Duis aute irure dolor in reprehenderit in voluptate velit esse.');
$output->warning('Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi.');
} |
@kbond superb! I'm starting to like it. Some proposals: Instead of this: $output->listElement(array(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod',
'tempor incididunt ut labore et dolore magna aliqua.'
));
$output->listElement(array(
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut',
'aliquip ex ea commodo.'
));
$output->listElement(array(
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt',
'mollit anim id est laborum.'
)); What about this: $output->list(array(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod ...',
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ...',
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt ...',
)); Instead of this: $output->caution(array(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit,',
'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.'
)); If you want to output a caution: $output->caution('Lorem ipsum dolor sit amet, consectetur adipisicing elit ...'); This would output as:
If you want to show two or more separated paragraphs inside the caution: $output->caution(array(
'Lorem ipsum dolor sit amet, consectetur adipisicing elit ...',
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris ...'
)); This would output as:
|
@javiereguiluz So I should add the logic to split the lines if they are too long? |
@kbond @javiereguiluz What about $output->listing(array('Lorem ipsum dolor...'))` |
@nicolas-grekas old! |
I have added Few notes/issues:
|
I was able to make the table helper match as close to the style guide as possible. |
->setCellHeaderFormat('%s') | ||
; | ||
|
||
Table::setStyleDefinition('symfony-style-guide', $styleGuideTableStyle); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I add this in Table::initStyles
?
I have done some refactoring to remove the |
Looking good! 👍 |
*/ | ||
class SymfonyStyle extends OutputStyle | ||
{ | ||
const MAX_LENGTH = 120; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor comment, ignore it at will: what about renaming this constant to MAX_LINE_LENGTH
?
Looking awesome :) is this going to make it into 2.6? |
@javiereguiluz, Fabien mentioned last week at SymfonyLive that we might be able to sneak this into 2.6 if we can finalize it in time... |
Whats the status of this? |
:( I hope this isnt dead. |
@kbond do you think you'll have time to rebase and finish this PR this week? If not, I totally understand, but we're in a hurry because this week ends the development period for Symfony 2.7 and it's our last chance to make it happen for 2.x branch. Thanks. |
Yes, I can work on this. Should it be rebased to the 2.7 branch then? Do I have to open a new PR? |
Closed in favor of #14057 |
…(kbond) This PR was squashed before being merged into the 2.7 branch (closes #14057). Discussion ---------- [RFC][Console] Added console style guide helpers (v2) *(Rebased to 2.7)* | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #12014, #12035, symfony/symfony-docs#4265 | License | MIT | Doc PR | todo ## Proposed API ### Code ```php // Symfony command protected function execute(InputInterface $input, OutputInterface $output) { $output = new SymfonyStyle($output); $output->title('Lorem Ipsum Dolor Sit Amet'); $output->text(array( 'Duis aute irure dolor in reprehenderit in voluptate velit esse', 'cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat' )); $output->ln(); $output->table(array('Name', 'Method', 'Scheme', 'Host', 'Path'), array( array('admin_post_new', 'ANY', 'ANY', 'ANY', '/admin/post/new'), array('admin_post_show', 'GET', 'ANY', 'ANY', '/admin/post/{id}'), array('admin_post_edit', 'ANY', 'ANY', 'ANY', '/admin/post/{id}/edit'), array('admin_post_delete', 'DELETE', 'ANY', 'ANY', '/admin/post/{id}'), )); $output->caution(array( 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.', 'foo' )); $output->section('Consectetur Adipisicing Elit Sed Do Eiusmod'); $output->listing(array( 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod, tempor incididunt ut labore et dolore magna aliqua.', 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.', 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' )); $customValidator = function ($value) { if ($value == 'foo') { throw new \Exception('cannot be foo'); } return $value; }; // hidden question $output->note($output->askHidden('Hidden question')); // choice questions $output->note($output->choice('Choice question no default', array('choice1', 'choice2'))); $output->note($output->choice('Choice question with default', array('choice1', 'choice2'), 'choice1')); // confirmation questions $output->note($output->confirm('Confirmation with yes default', true) ? 'yes' : 'no'); $output->note($output->confirm('Confirmation with no default', false) ? 'yes' : 'no'); // standard questions $output->note($output->ask('Question no default')); $output->note($output->ask('Question no default and custom validator', null, $customValidator)); $output->note($output->ask('Question with default', 'default')); $output->note($output->ask('Question with default and custom validator', 'default', $customValidator)); $output->note('Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat.'); $output->success('Lorem ipsum dolor sit amet, consectetur adipisicing elit'); $output->error('Duis aute irure dolor in reprehenderit in voluptate velit esse.'); $output->warning('Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi.'); $output->progressStart(100); for ($i = 0; $i < 100; $i++) { usleep(10000); $output->progressAdvance(); } $output->progressFinish(); } ``` ### Output ``` $ php app/console command Lorem Ipsum Dolor Sit Amet ========================== // Duis aute irure dolor in reprehenderit in voluptate velit esse // cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat ------------------- -------- -------- ------ ----------------------- Name Method Scheme Host Path ------------------- -------- -------- ------ ----------------------- admin_post_new ANY ANY ANY /admin/post/new admin_post_show GET ANY ANY /admin/post/{id} admin_post_edit ANY ANY ANY /admin/post/{id}/edit admin_post_delete DELETE ANY ANY /admin/post/{id} ------------------- -------- -------- ------ ----------------------- ! [CAUTION] Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et ! dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. Lorem ipsum dolor sit amet, ! consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim ! veniam, quis nostrud exercitation ullamco laboris. ! ! foo Consectetur Adipisicing Elit Sed Do Eiusmod ------------------------------------------- * Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod, tempor incididunt ut labore et dolore magna aliqua. * Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo. * Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Hidden question: > <f><o><o><enter> ! [NOTE] foo Choice question no default: [0] choice1 [1] choice2 > <enter> [ERROR] Value "" is invalid Choice question no default: [0] choice1 [1] choice2 > 0<enter> ! [NOTE] choice1 Choice question with default [choice1]: [0] choice1 [1] choice2 > 1<enter> ! [NOTE] choice2 Confirmation with yes default (yes/no) [yes]: > <enter> ! [NOTE] yes Confirmation with no default (yes/no) [no]: > <enter> ! [NOTE] no Question no default: > <enter> [ERROR] A value is required. Question no default: > foo<enter> ! [NOTE] foo Question no default and custom validator: > foo<enter> [ERROR] cannot be foo Question no default and custom validator: > <enter> [ERROR] A value is required. Question no default and custom validator: > foo<enter> [ERROR] cannot be foo Question no default and custom validator: > bar<enter> ! [NOTE] bar Question with default [default]: > <enter> ! [NOTE] default Question with default and custom validator [default]: > <enter> ! [NOTE] default ! [NOTE] Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ! Excepteur sint occaecat cupidatat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu ! fugiat nulla pariatur. Excepteur sint occaecat cupidatat. Duis aute irure dolor in reprehenderit in voluptate velit ! esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat. [OK] Lorem ipsum dolor sit amet, consectetur adipisicing elit [ERROR] Duis aute irure dolor in reprehenderit in voluptate velit esse. [WARNING] Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi. 100/100 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ``` ### Screenshots    Commits ------- 96b4210 [RFC][Console] Added console style guide helpers (v2)
Proposed API
Code
Output
Screenshots