Skip to content

Commit

Permalink
Allow \SensioLabs\AnsiConverter\AnsiToHtmlConverter to be injected
Browse files Browse the repository at this point in the history
As a consequence, the need to be able to set a theme at run time, rather
than at compile time is necessary.

Also added PHPUnit require-dev support and added tests to show the theme
can be injected and overridden.
  • Loading branch information
Richard Quadling authored and Richard Quadling committed Oct 10, 2019
1 parent 8b5d787 commit 667d0c4
Show file tree
Hide file tree
Showing 13 changed files with 369 additions and 86 deletions.
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
* text=auto eol=lf

.gitattributes export-ignore
.gitignore export-ignore
.php_cs export-ignore
.travis.yml export-ignore
phpunit.xml.dist export-ignore
SensioLabs/AnsiConverter/Tests/AnsiToHtmlConverterTest.php export-ignore
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
composer.lock
/vendor/
/.php_cs.cache
47 changes: 47 additions & 0 deletions .php_cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

$finder = PhpCsFixer\Finder::create()
->in(__DIR__);

$header = <<<TXT
This file is part of ansi-to-html.
(c) 2013 Fabien Potencier
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
TXT;

$rules = [
'@PSR2' => true,
'@Symfony' => true,
'cast_spaces' => [
'space' => 'none',
],
'concat_space' => [
'spacing' => 'none',
],
'native_function_invocation' => [
'scope' => 'namespaced',
],
'psr4' => true,
'phpdoc_align' => [
'align' => 'left',
],
'array_syntax' => [
'syntax' => 'short',
],
'header_comment' => [
'header' => $header,
'commentType' => PhpCsFixer\Fixer\Comment\HeaderCommentFixer::HEADER_PHPDOC,
],
'yoda_style' => false,
];

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules($rules)
->setFinder($finder)
->setCacheFile($cacheDir . '/.php_cs.cache');
44 changes: 44 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
sudo: false

language: php

php:
- 7.0
- 7.1
- 7.2
- 7.3
- hhvm

env:
- COMPOSER_FLAGS=--prefer-lowest

matrix:
exclude:
- php: hhvm
env: COMPOSER_FLAGS=--prefer-lowest
include:
- php: hhvm
env: COMPOSER_FLAGS=
- php: 7
env: PHPSTAN=1
allow_failures:
- php: nightly
- php: hhvm

cache:
directories:
- $HOME/.composer/cache
- $HOME/.php-cs-fixer

before_script:
- travis_retry composer self-update
- travis_retry composer update --no-interaction --prefer-source --prefer-stable ${COMPOSER_FLAGS}

script:
- vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
- composer cs
- composer sa

after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ which you can then use in your HTML document:
</html>
```

You can also override the theme by calling the `setTheme()` method with a new theme.

Twig Integration
----------------

Expand Down
131 changes: 92 additions & 39 deletions SensioLabs/AnsiConverter/AnsiToHtmlConverter.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

/*
/**
* This file is part of ansi-to-html.
*
* (c) 2013 Fabien Potencier
Expand All @@ -18,36 +18,45 @@
*/
class AnsiToHtmlConverter
{
/** @var Theme */
protected $theme;

/** @var string */
protected $charset;

/** @var bool */
protected $inlineStyles;

/** @var string[] */
protected $inlineColors;

/** @var string[] */
protected $colorNames;

public function __construct(Theme $theme = null, $inlineStyles = true, $charset = 'UTF-8')
{
$this->theme = null === $theme ? new Theme() : $theme;
$this->inlineStyles = $inlineStyles;
$this->charset = $charset;
$this->inlineColors = $this->theme->asArray();
$this->colorNames = array(
$this->setTheme($theme);
$this->setInlineStyles($inlineStyles);
$this->setCharset($charset);

$this->colorNames = [
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',
'', '',
'brblack', 'brred', 'brgreen', 'bryellow', 'brblue', 'brmagenta', 'brcyan', 'brwhite',
);
];
}

public function convert($text)
{
// remove cursor movement sequences
$text = preg_replace('#\e\[(K|s|u|2J|2K|\d+(A|B|C|D|E|F|G|J|K|S|T)|\d+;\d+(H|f))#', '', $text);
$text = \preg_replace('#\e\[(K|s|u|2J|2K|\d+(A|B|C|D|E|F|G|J|K|S|T)|\d+;\d+(H|f))#', '', $text);
// remove character set sequences
$text = preg_replace('#\e(\(|\))(A|B|[0-2])#', '', $text);
$text = \preg_replace('#\e(\(|\))(A|B|[0-2])#', '', $text);

$text = htmlspecialchars($text, PHP_VERSION_ID >= 50400 ? ENT_QUOTES | ENT_SUBSTITUTE : ENT_QUOTES, $this->charset);
$text = \htmlspecialchars($text, \PHP_VERSION_ID >= 50400 ? ENT_QUOTES | ENT_SUBSTITUTE : ENT_QUOTES, $this->charset);

// carriage return
$text = preg_replace('#^.*\r(?!\n)#m', '', $text);
$text = \preg_replace('#^.*\r(?!\n)#m', '', $text);

$tokens = $this->tokenize($text);

Expand All @@ -56,8 +65,8 @@ public function convert($text)
if ('backspace' == $token[0]) {
$j = $i;
while (--$j >= 0) {
if ('text' == $tokens[$j][0] && strlen($tokens[$j][1]) > 0) {
$tokens[$j][1] = substr($tokens[$j][1], 0, -1);
if ('text' == $tokens[$j][0] && \strlen($tokens[$j][1]) > 0) {
$tokens[$j][1] = \substr($tokens[$j][1], 0, -1);

break;
}
Expand All @@ -75,20 +84,35 @@ public function convert($text)
}

if ($this->inlineStyles) {
$html = sprintf('<span style="background-color: %s; color: %s">%s</span>', $this->inlineColors['black'], $this->inlineColors['white'], $html);
$html = \sprintf('<span style="background-color: %s; color: %s">%s</span>', $this->inlineColors['black'], $this->inlineColors['white'], $html);
} else {
$html = sprintf('<span class="ansi_color_bg_black ansi_color_fg_white">%s</span>', $html);
$html = \sprintf('<span class="ansi_color_bg_black ansi_color_fg_white">%s</span>', $html);
}

// remove empty span
$html = preg_replace('#<span[^>]*></span>#', '', $html);
$html = \preg_replace('#<span[^>]*></span>#', '', $html);

return $html;
}

public function getTheme()
protected function tokenize($text)
{
return $this->theme;
$tokens = [];
\preg_match_all('/(?:\e\[(.*?)m|(\x08))/', $text, $matches, PREG_OFFSET_CAPTURE);

$offset = 0;
foreach ($matches[0] as $i => $match) {
if ($match[1] - $offset > 0) {
$tokens[] = ['text', \substr($text, $offset, $match[1] - $offset)];
}
$tokens[] = ["\x08" == $match[0] ? 'backspace' : 'color', $matches[1][$i][0]];
$offset = $match[1] + \strlen($match[0]);
}
if ($offset < \strlen($text)) {
$tokens[] = ['text', \substr($text, $offset)];
}

return $tokens;
}

protected function convertAnsiToColor($ansi)
Expand All @@ -97,7 +121,7 @@ protected function convertAnsiToColor($ansi)
$fg = 7;
$as = '';
if ('0' != $ansi && '' != $ansi) {
$options = explode(';', $ansi);
$options = \explode(';', $ansi);

foreach ($options as $option) {
if ($option >= 30 && $option < 38) {
Expand All @@ -112,46 +136,75 @@ protected function convertAnsiToColor($ansi)
}

// options: bold => 1, underscore => 4, blink => 5, reverse => 7, conceal => 8
if (in_array(1, $options)) {
if (\in_array(1, $options)) {
$fg += 10;
$bg += 10;
}

if (in_array(4, $options)) {
if (\in_array(4, $options)) {
$as = '; text-decoration: underline';
}

if (in_array(7, $options)) {
if (\in_array(7, $options)) {
$tmp = $fg;
$fg = $bg;
$bg = $tmp;
}
}

if ($this->inlineStyles) {
return sprintf('</span><span style="background-color: %s; color: %s%s">', $this->inlineColors[$this->colorNames[$bg]], $this->inlineColors[$this->colorNames[$fg]], $as);
return \sprintf('</span><span style="background-color: %s; color: %s%s">', $this->inlineColors[$this->colorNames[$bg]], $this->inlineColors[$this->colorNames[$fg]], $as);
} else {
return sprintf('</span><span class="ansi_color_bg_%s ansi_color_fg_%s">', $this->colorNames[$bg], $this->colorNames[$fg]);
return \sprintf('</span><span class="ansi_color_bg_%s ansi_color_fg_%s">', $this->colorNames[$bg], $this->colorNames[$fg]);
}
}

protected function tokenize($text)
/**
* @return Theme
*/
public function getTheme()
{
$tokens = array();
preg_match_all("/(?:\e\[(.*?)m|(\x08))/", $text, $matches, PREG_OFFSET_CAPTURE);
return $this->theme;
}

$offset = 0;
foreach ($matches[0] as $i => $match) {
if ($match[1] - $offset > 0) {
$tokens[] = array('text', substr($text, $offset, $match[1] - $offset));
}
$tokens[] = array("\x08" == $match[0] ? 'backspace' : 'color', $matches[1][$i][0]);
$offset = $match[1] + strlen($match[0]);
}
if ($offset < strlen($text)) {
$tokens[] = array('text', substr($text, $offset));
}
/**
* @param Theme|null $theme
*/
public function setTheme(Theme $theme = null)
{
$this->theme = null === $theme ? new Theme() : $theme;
$this->inlineColors = $this->theme->asArray();
}

return $tokens;
/**
* @return bool
*/
public function isInlineStyles()
{
return $this->inlineStyles;
}

/**
* @param bool $inlineStyles
*/
public function setInlineStyles($inlineStyles)
{
$this->inlineStyles = $inlineStyles;
}

/**
* @return string
*/
public function getCharset()
{
return $this->charset;
}

/**
* @param string $charset
*/
public function setCharset($charset)
{
$this->charset = $charset;
}
}
26 changes: 19 additions & 7 deletions SensioLabs/AnsiConverter/Bridge/Twig/AnsiExtension.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
<?php

/**
* This file is part of ansi-to-html.
*
* (c) 2013 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace SensioLabs\AnsiConverter\Bridge\Twig;

use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

class AnsiExtension extends \Twig_Extension
class AnsiExtension extends AbstractExtension
{
private $converter;

Expand All @@ -15,16 +27,16 @@ public function __construct(AnsiToHtmlConverter $converter = null)

public function getFilters()
{
return array(
new \Twig_SimpleFilter('ansi_to_html', array($this, 'ansiToHtml'), array('is_safe' => array('html'))),
);
return [
new TwigFilter('ansi_to_html', [$this, 'ansiToHtml'], ['is_safe' => ['html']]),
];
}

public function getFunctions()
{
return array(
new \Twig_SimpleFunction('ansi_css', array($this, 'css'), array('is_safe' => array('css'))),
);
return [
new TwigFunction('ansi_css', [$this, 'css'], ['is_safe' => ['css']]),
];
}

public function ansiToHtml($string)
Expand Down
Loading

0 comments on commit 667d0c4

Please sign in to comment.