Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c62069b

Browse filesBrowse files
committed
bug #14623 [Console] SymfonyStyle : fix & automate block gaps. (ogizanagi)
This PR was merged into the 2.7 branch. Discussion ---------- [Console] SymfonyStyle : fix & automate block gaps. | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - --- Depends on #14741 --- ## What it does - autoprepend appropriate blocks (like cautions, titles, sections, ...) by the correct number of blank lines considering history. - handle automatically most of the SymfonyStyle guide line breaks and gaps. Fix things such as unwanted double blank lines between titles and admonitions. - test outputs - fix an issue using questions with SymfonyStyle, which should not output extra blank lines when using with a non-interactive input. ## Description `SymfonyStyle` is great, but there are some issues, mostly when using blocks (text blocks, titles and admonitions): some extra blank lines might be generated. Plus, on the contrary, some line breaks or blank lines around blocks are missing, and the developer need to handle this himself by polluting his code with ugly `if` and `newLine()` statements. ### Before / After : ![screenshot 2015-05-13 a 00 11 59](https://cloud.githubusercontent.com/assets/2211145/7600572/ccfa8904-f90c-11e4-999f-d89612360424.PNG) As you can see, it's still up to the developper to end his command by a blank line (unless using a block like `SymfonyStyle::success()`) in order to distinct different commands outputs more efficiently. Everything else is now handled properly, and automatically, according to the rules exposed in the symfony console style guide published some time ago by @javiereguiluz . Questions (not exposed in the above output) are considered as blocks, and follow, for instance, the same conditions than admonitions: 1 blank line before and after, no more (although, you'll still be able to output more blank lines yourself, using `newLine`). Commits ------- fc598ff [Console] SymfonyStyle : fix & automate block gaps. 260702e [Console] SymfonyStyle : Improve EOL consistency by relying on output instance
2 parents 807d192 + fc598ff commit c62069b
Copy full SHA for c62069b

File tree

Expand file treeCollapse file tree

18 files changed

+367
-10
lines changed
Filter options
Expand file treeCollapse file tree

18 files changed

+367
-10
lines changed

‎src/Symfony/Component/Console/Style/SymfonyStyle.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Style/SymfonyStyle.php
+90-10Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
1919
use Symfony\Component\Console\Helper\Table;
2020
use Symfony\Component\Console\Input\InputInterface;
21+
use Symfony\Component\Console\Output\BufferedOutput;
2122
use Symfony\Component\Console\Output\OutputInterface;
2223
use Symfony\Component\Console\Question\ChoiceQuestion;
2324
use Symfony\Component\Console\Question\ConfirmationQuestion;
@@ -36,6 +37,7 @@ class SymfonyStyle extends OutputStyle
3637
private $questionHelper;
3738
private $progressBar;
3839
private $lineLength;
40+
private $bufferedOutput;
3941

4042
/**
4143
* @param InputInterface $input
@@ -45,6 +47,7 @@ public function __construct(InputInterface $input, OutputInterface $output)
4547
{
4648
$this->input = $input;
4749
$this->lineLength = min($this->getTerminalWidth(), self::MAX_LINE_LENGTH);
50+
$this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter());
4851

4952
parent::__construct($output);
5053
}
@@ -60,6 +63,7 @@ public function __construct(InputInterface $input, OutputInterface $output)
6063
*/
6164
public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false)
6265
{
66+
$this->autoPrependBlock();
6367
$messages = is_array($messages) ? array_values($messages) : array($messages);
6468
$lines = array();
6569

@@ -71,7 +75,7 @@ public function block($messages, $type = null, $style = null, $prefix = ' ', $pa
7175
// wrap and add newlines for each element
7276
foreach ($messages as $key => $message) {
7377
$message = OutputFormatter::escape($message);
74-
$lines = array_merge($lines, explode("\n", wordwrap($message, $this->lineLength - Helper::strlen($prefix))));
78+
$lines = array_merge($lines, explode(PHP_EOL, wordwrap($message, $this->lineLength - Helper::strlen($prefix), PHP_EOL)));
7579

7680
if (count($messages) > 1 && $key < count($messages) - 1) {
7781
$lines[] = '';
@@ -92,44 +96,57 @@ public function block($messages, $type = null, $style = null, $prefix = ' ', $pa
9296
}
9397
}
9498

95-
$this->writeln(implode("\n", $lines)."\n");
99+
$this->writeln($lines);
100+
$this->newLine();
96101
}
97102

98103
/**
99104
* {@inheritdoc}
100105
*/
101106
public function title($message)
102107
{
103-
$this->writeln(sprintf("\n<comment>%s</>\n<comment>%s</>\n", $message, str_repeat('=', strlen($message))));
108+
$this->autoPrependBlock();
109+
$this->writeln(array(
110+
sprintf('<comment>%s</>', $message),
111+
sprintf('<comment>%s</>', str_repeat('=', strlen($message))),
112+
));
113+
$this->newLine();
104114
}
105115

106116
/**
107117
* {@inheritdoc}
108118
*/
109119
public function section($message)
110120
{
111-
$this->writeln(sprintf("<comment>%s</>\n<comment>%s</>\n", $message, str_repeat('-', strlen($message))));
121+
$this->autoPrependBlock();
122+
$this->writeln(array(
123+
sprintf('<comment>%s</>', $message),
124+
sprintf('<comment>%s</>', str_repeat('-', strlen($message))),
125+
));
126+
$this->newLine();
112127
}
113128

114129
/**
115130
* {@inheritdoc}
116131
*/
117132
public function listing(array $elements)
118133
{
134+
$this->autoPrependText();
119135
$elements = array_map(function ($element) {
120-
return sprintf(' * %s', $element);
121-
},
122-
$elements
123-
);
136+
return sprintf(' * %s', $element);
137+
}, $elements);
124138

125-
$this->writeln(implode("\n", $elements)."\n");
139+
$this->writeln($elements);
140+
$this->newLine();
126141
}
127142

128143
/**
129144
* {@inheritdoc}
130145
*/
131146
public function text($message)
132147
{
148+
$this->autoPrependText();
149+
133150
if (!is_array($message)) {
134151
$this->writeln(sprintf(' // %s', $message));
135152

@@ -292,17 +309,51 @@ public function createProgressBar($max = 0)
292309
*/
293310
public function askQuestion(Question $question)
294311
{
312+
if ($this->input->isInteractive()) {
313+
$this->autoPrependBlock();
314+
}
315+
295316
if (!$this->questionHelper) {
296317
$this->questionHelper = new SymfonyQuestionHelper();
297318
}
298319

299320
$answer = $this->questionHelper->ask($this->input, $this, $question);
300321

301-
$this->newLine();
322+
if ($this->input->isInteractive()) {
323+
$this->newLine();
324+
$this->bufferedOutput->write("\n");
325+
}
302326

303327
return $answer;
304328
}
305329

330+
/**
331+
* {@inheritdoc}
332+
*/
333+
public function writeln($messages, $type = self::OUTPUT_NORMAL)
334+
{
335+
parent::writeln($messages, $type);
336+
$this->bufferedOutput->writeln($this->reduceBuffer($messages), $type);
337+
}
338+
339+
/**
340+
* {@inheritdoc}
341+
*/
342+
public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
343+
{
344+
parent::write($messages, $newline, $type);
345+
$this->bufferedOutput->write($this->reduceBuffer($messages), $newline, $type);
346+
}
347+
348+
/**
349+
* {@inheritdoc}
350+
*/
351+
public function newLine($count = 1)
352+
{
353+
parent::newLine($count);
354+
$this->bufferedOutput->write(str_repeat("\n", $count));
355+
}
356+
306357
/**
307358
* @return ProgressBar
308359
*/
@@ -322,4 +373,33 @@ private function getTerminalWidth()
322373

323374
return $dimensions[0] ?: self::MAX_LINE_LENGTH;
324375
}
376+
377+
private function autoPrependBlock()
378+
{
379+
$chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);
380+
381+
if (false === $chars) {
382+
return $this->newLine(); //empty history, so we should start with a new line.
383+
}
384+
//Prepend new line for each non LF chars (This means no blank line was output before)
385+
$this->newLine(2 - substr_count($chars, "\n"));
386+
}
387+
388+
private function autoPrependText()
389+
{
390+
$fetched = $this->bufferedOutput->fetch();
391+
//Prepend new line if last char isn't EOL:
392+
if ("\n" !== substr($fetched, -1)) {
393+
$this->newLine();
394+
}
395+
}
396+
397+
private function reduceBuffer($messages)
398+
{
399+
// We need to know if the two last chars are PHP_EOL
400+
// Preserve the last 4 chars inserted (PHP_EOL on windows is two chars) in the history buffer
401+
return array_map(function ($value) {
402+
return substr($value, -4);
403+
}, array_merge(array($this->bufferedOutput->fetch()), (array) $messages));
404+
}
325405
}
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
use Symfony\Component\Console\Input\InputInterface;
4+
use Symfony\Component\Console\Output\OutputInterface;
5+
use Symfony\Component\Console\Style\SymfonyStyle;
6+
7+
//Ensure has single blank line at start when using block element
8+
return function (InputInterface $input, OutputInterface $output) {
9+
$output = new SymfonyStyle($input, $output);
10+
$output->caution('Lorem ipsum dolor sit amet');
11+
};
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
use Symfony\Component\Console\Input\InputInterface;
4+
use Symfony\Component\Console\Output\OutputInterface;
5+
use Symfony\Component\Console\Style\SymfonyStyle;
6+
7+
//Ensure has single blank line between titles and blocks
8+
return function (InputInterface $input, OutputInterface $output) {
9+
$output = new SymfonyStyle($input, $output);
10+
$output->title('Title');
11+
$output->warning('Lorem ipsum dolor sit amet');
12+
$output->title('Title');
13+
};
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
use Symfony\Component\Console\Input\InputInterface;
4+
use Symfony\Component\Console\Output\OutputInterface;
5+
use Symfony\Component\Console\Style\SymfonyStyle;
6+
7+
//Ensure has single blank line between blocks
8+
return function (InputInterface $input, OutputInterface $output) {
9+
$output = new SymfonyStyle($input, $output);
10+
$output->warning('Warning');
11+
$output->caution('Caution');
12+
$output->error('Error');
13+
$output->success('Success');
14+
$output->note('Note');
15+
$output->block('Custom block', 'CUSTOM', 'fg=white;bg=green', 'X ', true);
16+
};
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
use Symfony\Component\Console\Input\InputInterface;
4+
use Symfony\Component\Console\Output\OutputInterface;
5+
use Symfony\Component\Console\Style\SymfonyStyle;
6+
7+
//Ensure has single blank line between two titles
8+
return function (InputInterface $input, OutputInterface $output) {
9+
$output = new SymfonyStyle($input, $output);
10+
$output->title('First title');
11+
$output->title('Second title');
12+
};
+34Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
use Symfony\Component\Console\Input\InputInterface;
4+
use Symfony\Component\Console\Output\OutputInterface;
5+
use Symfony\Component\Console\Style\SymfonyStyle;
6+
7+
//Ensure has single blank line after any text and a title
8+
return function (InputInterface $input, OutputInterface $output) {
9+
$output = new SymfonyStyle($input, $output);
10+
11+
$output->write('Lorem ipsum dolor sit amet');
12+
$output->title('First title');
13+
14+
$output->writeln('Lorem ipsum dolor sit amet');
15+
$output->title('Second title');
16+
17+
$output->write('Lorem ipsum dolor sit amet');
18+
$output->write('');
19+
$output->title('Third title');
20+
21+
//Ensure edge case by appending empty strings to history:
22+
$output->write('Lorem ipsum dolor sit amet');
23+
$output->write(array('', '', ''));
24+
$output->title('Fourth title');
25+
26+
//Ensure have manual control over number of blank lines:
27+
$output->writeln('Lorem ipsum dolor sit amet');
28+
$output->writeln(array('', '')); //Should append an extra blank line
29+
$output->title('Fifth title');
30+
31+
$output->writeln('Lorem ipsum dolor sit amet');
32+
$output->newLine(2); //Should append an extra blank line
33+
$output->title('Fifth title');
34+
};
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
use Symfony\Component\Console\Input\InputInterface;
4+
use Symfony\Component\Console\Output\OutputInterface;
5+
use Symfony\Component\Console\Style\SymfonyStyle;
6+
7+
//Ensure has proper line ending before outputing a text block like with SymfonyStyle::listing() or SymfonyStyle::text()
8+
return function (InputInterface $input, OutputInterface $output) {
9+
$output = new SymfonyStyle($input, $output);
10+
11+
$output->writeln('Lorem ipsum dolor sit amet');
12+
$output->listing(array(
13+
'Lorem ipsum dolor sit amet',
14+
'consectetur adipiscing elit',
15+
));
16+
17+
//Even using write:
18+
$output->write('Lorem ipsum dolor sit amet');
19+
$output->listing(array(
20+
'Lorem ipsum dolor sit amet',
21+
'consectetur adipiscing elit',
22+
));
23+
24+
$output->write('Lorem ipsum dolor sit amet');
25+
$output->text(array(
26+
'Lorem ipsum dolor sit amet',
27+
'consectetur adipiscing elit',
28+
));
29+
};
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
use Symfony\Component\Console\Input\InputInterface;
4+
use Symfony\Component\Console\Output\OutputInterface;
5+
use Symfony\Component\Console\Style\SymfonyStyle;
6+
7+
//Ensure has proper blank line after text block when using a block like with SymfonyStyle::success
8+
return function (InputInterface $input, OutputInterface $output) {
9+
$output = new SymfonyStyle($input, $output);
10+
11+
$output->listing(array(
12+
'Lorem ipsum dolor sit amet',
13+
'consectetur adipiscing elit',
14+
));
15+
$output->success('Lorem ipsum dolor sit amet');
16+
};
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
use Symfony\Component\Console\Input\InputInterface;
4+
use Symfony\Component\Console\Output\OutputInterface;
5+
use Symfony\Component\Console\Style\SymfonyStyle;
6+
7+
//Ensure questions do not output anything when input is non-interactive
8+
return function (InputInterface $input, OutputInterface $output) {
9+
$output = new SymfonyStyle($input, $output);
10+
$output->title('Title');
11+
$output->askHidden('Hidden question');
12+
$output->choice('Choice question with default', array('choice1', 'choice2'), 'choice1');
13+
$output->confirm('Confirmation with yes default', true);
14+
$output->text('Duis aute irure dolor in reprehenderit in voluptate velit esse');
15+
};
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
! [CAUTION] Lorem ipsum dolor sit amet
3+
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
Title
3+
=====
4+
5+
[WARNING] Lorem ipsum dolor sit amet
6+
7+
Title
8+
=====
9+
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
[WARNING] Warning
3+
4+
! [CAUTION] Caution
5+
6+
[ERROR] Error
7+
8+
[OK] Success
9+
10+
! [NOTE] Note
11+
12+
X [CUSTOM] Custom block
13+
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
First title
3+
===========
4+
5+
Second title
6+
============
7+

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.