feature #29168 [Console] Add hyperlinks support (ostrolucky)

This PR was squashed before being merged into the 4.3-dev branch (closes #29168).

Discussion
----------

[Console] Add hyperlinks support

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #21207
| License       | MIT
| Doc PR        |

For details about this see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda

Here's one idea/use case which could utilize this feature (not implemented in this PR as it requires more work)
![stack trace with anchors](https://user-images.githubusercontent.com/496233/48305600-00d4c300-e52e-11e8-94e6-33713ff09d50.png)

I checked this in cmd.exe as well and no sideffects there

Commits
-------

db750ed8ae [Console] Add hyperlinks support
This commit is contained in:
Fabien Potencier 2018-12-10 04:47:54 +01:00
commit 58b29d6a11
4 changed files with 41 additions and 38 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
4.3.0
-----
* added support for hyperlinks
4.2.0
-----

View File

@ -141,7 +141,7 @@ class OutputFormatter implements WrappableOutputFormatterInterface
{
$offset = 0;
$output = '';
$tagRegex = '[a-z][a-z0-9,_=;-]*+';
$tagRegex = '[a-z][^<>]*+';
$currentLineLength = 0;
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[0] as $i => $match) {
@ -215,6 +215,8 @@ class OutputFormatter implements WrappableOutputFormatterInterface
$style->setForeground($match[1]);
} elseif ('bg' == $match[0]) {
$style->setBackground($match[1]);
} elseif ('href' === $match[0]) {
$style->setHref($match[1]);
} elseif ('options' === $match[0]) {
preg_match_all('([^,;]+)', $match[1], $options);
$options = array_shift($options);

View File

@ -52,6 +52,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
private $foreground;
private $background;
private $href;
private $options = array();
/**
@ -118,6 +119,11 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
$this->background = static::$availableBackgroundColors[$color];
}
public function setHref(string $url): void
{
$this->href = $url;
}
/**
* Sets some specific style option.
*
@ -187,11 +193,14 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
$setCodes[] = $this->background['set'];
$unsetCodes[] = $this->background['unset'];
}
if (\count($this->options)) {
foreach ($this->options as $option) {
$setCodes[] = $option['set'];
$unsetCodes[] = $option['unset'];
}
foreach ($this->options as $option) {
$setCodes[] = $option['set'];
$unsetCodes[] = $option['unset'];
}
if (null !== $this->href) {
$text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";
}
if (0 === \count($setCodes)) {

View File

@ -228,7 +228,7 @@ class OutputFormatterTest extends TestCase
);
}
public function testNotDecoratedFormatter()
public function testFormatterHasStyles()
{
$formatter = new OutputFormatter(false);
@ -236,39 +236,26 @@ class OutputFormatterTest extends TestCase
$this->assertTrue($formatter->hasStyle('info'));
$this->assertTrue($formatter->hasStyle('comment'));
$this->assertTrue($formatter->hasStyle('question'));
}
$this->assertEquals(
'some error', $formatter->format('<error>some error</error>')
);
$this->assertEquals(
'some info', $formatter->format('<info>some info</info>')
);
$this->assertEquals(
'some comment', $formatter->format('<comment>some comment</comment>')
);
$this->assertEquals(
'some question', $formatter->format('<question>some question</question>')
);
$this->assertEquals(
'some text with inline style', $formatter->format('<fg=red>some text with inline style</>')
);
/**
* @dataProvider provideDecoratedAndNonDecoratedOutput
*/
public function testNotDecoratedFormatter(string $input, string $expectedNonDecoratedOutput, string $expectedDecoratedOutput)
{
$this->assertEquals($expectedDecoratedOutput, (new OutputFormatter(true))->format($input));
$this->assertEquals($expectedNonDecoratedOutput, (new OutputFormatter(false))->format($input));
}
$formatter->setDecorated(true);
$this->assertEquals(
"\033[37;41msome error\033[39;49m", $formatter->format('<error>some error</error>')
);
$this->assertEquals(
"\033[32msome info\033[39m", $formatter->format('<info>some info</info>')
);
$this->assertEquals(
"\033[33msome comment\033[39m", $formatter->format('<comment>some comment</comment>')
);
$this->assertEquals(
"\033[30;46msome question\033[39;49m", $formatter->format('<question>some question</question>')
);
$this->assertEquals(
"\033[31msome text with inline style\033[39m", $formatter->format('<fg=red>some text with inline style</>')
public function provideDecoratedAndNonDecoratedOutput()
{
return array(
array('<error>some error</error>', 'some error', "\033[37;41msome error\033[39;49m"),
array('<info>some info</info>', 'some info', "\033[32msome info\033[39m"),
array('<comment>some comment</comment>', 'some comment', "\033[33msome comment\033[39m"),
array('<question>some question</question>', 'some question', "\033[30;46msome question\033[39;49m"),
array('<fg=red>some text with inline style</>', 'some text with inline style', "\033[31msome text with inline style\033[39m"),
array('<href=idea://open/?file=/path/somefile.php&line=12>some URL</>', 'some URL', "\033]8;;idea://open/?file=/path/somefile.php&line=12\033\\some URL\033]8;;\033\\"),
);
}