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

Allow \SensioLabs\AnsiConverter\AnsiToHtmlConverter to be injected #14

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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