Skip to content

Commit 667d0c4

Browse files
committed
Allow \SensioLabs\AnsiConverter\AnsiToHtmlConverter to be injected
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.
1 parent 8b5d787 commit 667d0c4

File tree

13 files changed

+369
-86
lines changed

13 files changed

+369
-86
lines changed

.gitattributes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
* text=auto eol=lf
2+
3+
.gitattributes export-ignore
4+
.gitignore export-ignore
5+
.php_cs export-ignore
6+
.travis.yml export-ignore
7+
phpunit.xml.dist export-ignore
8+
SensioLabs/AnsiConverter/Tests/AnsiToHtmlConverterTest.php export-ignore

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
composer.lock
22
/vendor/
3+
/.php_cs.cache

.php_cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
$finder = PhpCsFixer\Finder::create()
4+
->in(__DIR__);
5+
6+
$header = <<<TXT
7+
This file is part of ansi-to-html.
8+
9+
(c) 2013 Fabien Potencier
10+
11+
For the full copyright and license information, please view the LICENSE
12+
file that was distributed with this source code.
13+
TXT;
14+
15+
$rules = [
16+
'@PSR2' => true,
17+
'@Symfony' => true,
18+
'cast_spaces' => [
19+
'space' => 'none',
20+
],
21+
'concat_space' => [
22+
'spacing' => 'none',
23+
],
24+
'native_function_invocation' => [
25+
'scope' => 'namespaced',
26+
],
27+
'psr4' => true,
28+
'phpdoc_align' => [
29+
'align' => 'left',
30+
],
31+
'array_syntax' => [
32+
'syntax' => 'short',
33+
],
34+
'header_comment' => [
35+
'header' => $header,
36+
'commentType' => PhpCsFixer\Fixer\Comment\HeaderCommentFixer::HEADER_PHPDOC,
37+
],
38+
'yoda_style' => false,
39+
];
40+
41+
$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;
42+
43+
return PhpCsFixer\Config::create()
44+
->setRiskyAllowed(true)
45+
->setRules($rules)
46+
->setFinder($finder)
47+
->setCacheFile($cacheDir . '/.php_cs.cache');

.travis.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
sudo: false
2+
3+
language: php
4+
5+
php:
6+
- 7.0
7+
- 7.1
8+
- 7.2
9+
- 7.3
10+
- hhvm
11+
12+
env:
13+
- COMPOSER_FLAGS=--prefer-lowest
14+
15+
matrix:
16+
exclude:
17+
- php: hhvm
18+
env: COMPOSER_FLAGS=--prefer-lowest
19+
include:
20+
- php: hhvm
21+
env: COMPOSER_FLAGS=
22+
- php: 7
23+
env: PHPSTAN=1
24+
allow_failures:
25+
- php: nightly
26+
- php: hhvm
27+
28+
cache:
29+
directories:
30+
- $HOME/.composer/cache
31+
- $HOME/.php-cs-fixer
32+
33+
before_script:
34+
- travis_retry composer self-update
35+
- travis_retry composer update --no-interaction --prefer-source --prefer-stable ${COMPOSER_FLAGS}
36+
37+
script:
38+
- vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
39+
- composer cs
40+
- composer sa
41+
42+
after_script:
43+
- wget https://scrutinizer-ci.com/ocular.phar
44+
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ which you can then use in your HTML document:
6868
</html>
6969
```
7070

71+
You can also override the theme by calling the `setTheme()` method with a new theme.
72+
7173
Twig Integration
7274
----------------
7375

Lines changed: 92 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
/*
3+
/**
44
* This file is part of ansi-to-html.
55
*
66
* (c) 2013 Fabien Potencier
@@ -18,36 +18,45 @@
1818
*/
1919
class AnsiToHtmlConverter
2020
{
21+
/** @var Theme */
2122
protected $theme;
23+
24+
/** @var string */
2225
protected $charset;
26+
27+
/** @var bool */
2328
protected $inlineStyles;
29+
30+
/** @var string[] */
2431
protected $inlineColors;
32+
33+
/** @var string[] */
2534
protected $colorNames;
2635

2736
public function __construct(Theme $theme = null, $inlineStyles = true, $charset = 'UTF-8')
2837
{
29-
$this->theme = null === $theme ? new Theme() : $theme;
30-
$this->inlineStyles = $inlineStyles;
31-
$this->charset = $charset;
32-
$this->inlineColors = $this->theme->asArray();
33-
$this->colorNames = array(
38+
$this->setTheme($theme);
39+
$this->setInlineStyles($inlineStyles);
40+
$this->setCharset($charset);
41+
42+
$this->colorNames = [
3443
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white',
3544
'', '',
3645
'brblack', 'brred', 'brgreen', 'bryellow', 'brblue', 'brmagenta', 'brcyan', 'brwhite',
37-
);
46+
];
3847
}
3948

4049
public function convert($text)
4150
{
4251
// remove cursor movement sequences
43-
$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);
52+
$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);
4453
// remove character set sequences
45-
$text = preg_replace('#\e(\(|\))(A|B|[0-2])#', '', $text);
54+
$text = \preg_replace('#\e(\(|\))(A|B|[0-2])#', '', $text);
4655

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

4958
// carriage return
50-
$text = preg_replace('#^.*\r(?!\n)#m', '', $text);
59+
$text = \preg_replace('#^.*\r(?!\n)#m', '', $text);
5160

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

@@ -56,8 +65,8 @@ public function convert($text)
5665
if ('backspace' == $token[0]) {
5766
$j = $i;
5867
while (--$j >= 0) {
59-
if ('text' == $tokens[$j][0] && strlen($tokens[$j][1]) > 0) {
60-
$tokens[$j][1] = substr($tokens[$j][1], 0, -1);
68+
if ('text' == $tokens[$j][0] && \strlen($tokens[$j][1]) > 0) {
69+
$tokens[$j][1] = \substr($tokens[$j][1], 0, -1);
6170

6271
break;
6372
}
@@ -75,20 +84,35 @@ public function convert($text)
7584
}
7685

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

8392
// remove empty span
84-
$html = preg_replace('#<span[^>]*></span>#', '', $html);
93+
$html = \preg_replace('#<span[^>]*></span>#', '', $html);
8594

8695
return $html;
8796
}
8897

89-
public function getTheme()
98+
protected function tokenize($text)
9099
{
91-
return $this->theme;
100+
$tokens = [];
101+
\preg_match_all('/(?:\e\[(.*?)m|(\x08))/', $text, $matches, PREG_OFFSET_CAPTURE);
102+
103+
$offset = 0;
104+
foreach ($matches[0] as $i => $match) {
105+
if ($match[1] - $offset > 0) {
106+
$tokens[] = ['text', \substr($text, $offset, $match[1] - $offset)];
107+
}
108+
$tokens[] = ["\x08" == $match[0] ? 'backspace' : 'color', $matches[1][$i][0]];
109+
$offset = $match[1] + \strlen($match[0]);
110+
}
111+
if ($offset < \strlen($text)) {
112+
$tokens[] = ['text', \substr($text, $offset)];
113+
}
114+
115+
return $tokens;
92116
}
93117

94118
protected function convertAnsiToColor($ansi)
@@ -97,7 +121,7 @@ protected function convertAnsiToColor($ansi)
97121
$fg = 7;
98122
$as = '';
99123
if ('0' != $ansi && '' != $ansi) {
100-
$options = explode(';', $ansi);
124+
$options = \explode(';', $ansi);
101125

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

114138
// options: bold => 1, underscore => 4, blink => 5, reverse => 7, conceal => 8
115-
if (in_array(1, $options)) {
139+
if (\in_array(1, $options)) {
116140
$fg += 10;
117141
$bg += 10;
118142
}
119143

120-
if (in_array(4, $options)) {
144+
if (\in_array(4, $options)) {
121145
$as = '; text-decoration: underline';
122146
}
123147

124-
if (in_array(7, $options)) {
148+
if (\in_array(7, $options)) {
125149
$tmp = $fg;
126150
$fg = $bg;
127151
$bg = $tmp;
128152
}
129153
}
130154

131155
if ($this->inlineStyles) {
132-
return sprintf('</span><span style="background-color: %s; color: %s%s">', $this->inlineColors[$this->colorNames[$bg]], $this->inlineColors[$this->colorNames[$fg]], $as);
156+
return \sprintf('</span><span style="background-color: %s; color: %s%s">', $this->inlineColors[$this->colorNames[$bg]], $this->inlineColors[$this->colorNames[$fg]], $as);
133157
} else {
134-
return sprintf('</span><span class="ansi_color_bg_%s ansi_color_fg_%s">', $this->colorNames[$bg], $this->colorNames[$fg]);
158+
return \sprintf('</span><span class="ansi_color_bg_%s ansi_color_fg_%s">', $this->colorNames[$bg], $this->colorNames[$fg]);
135159
}
136160
}
137161

138-
protected function tokenize($text)
162+
/**
163+
* @return Theme
164+
*/
165+
public function getTheme()
139166
{
140-
$tokens = array();
141-
preg_match_all("/(?:\e\[(.*?)m|(\x08))/", $text, $matches, PREG_OFFSET_CAPTURE);
167+
return $this->theme;
168+
}
142169

143-
$offset = 0;
144-
foreach ($matches[0] as $i => $match) {
145-
if ($match[1] - $offset > 0) {
146-
$tokens[] = array('text', substr($text, $offset, $match[1] - $offset));
147-
}
148-
$tokens[] = array("\x08" == $match[0] ? 'backspace' : 'color', $matches[1][$i][0]);
149-
$offset = $match[1] + strlen($match[0]);
150-
}
151-
if ($offset < strlen($text)) {
152-
$tokens[] = array('text', substr($text, $offset));
153-
}
170+
/**
171+
* @param Theme|null $theme
172+
*/
173+
public function setTheme(Theme $theme = null)
174+
{
175+
$this->theme = null === $theme ? new Theme() : $theme;
176+
$this->inlineColors = $this->theme->asArray();
177+
}
154178

155-
return $tokens;
179+
/**
180+
* @return bool
181+
*/
182+
public function isInlineStyles()
183+
{
184+
return $this->inlineStyles;
185+
}
186+
187+
/**
188+
* @param bool $inlineStyles
189+
*/
190+
public function setInlineStyles($inlineStyles)
191+
{
192+
$this->inlineStyles = $inlineStyles;
193+
}
194+
195+
/**
196+
* @return string
197+
*/
198+
public function getCharset()
199+
{
200+
return $this->charset;
201+
}
202+
203+
/**
204+
* @param string $charset
205+
*/
206+
public function setCharset($charset)
207+
{
208+
$this->charset = $charset;
156209
}
157210
}

SensioLabs/AnsiConverter/Bridge/Twig/AnsiExtension.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
<?php
22

3+
/**
4+
* This file is part of ansi-to-html.
5+
*
6+
* (c) 2013 Fabien Potencier
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
312
namespace SensioLabs\AnsiConverter\Bridge\Twig;
413

514
use SensioLabs\AnsiConverter\AnsiToHtmlConverter;
15+
use Twig\Extension\AbstractExtension;
16+
use Twig\TwigFilter;
17+
use Twig\TwigFunction;
618

7-
class AnsiExtension extends \Twig_Extension
19+
class AnsiExtension extends AbstractExtension
820
{
921
private $converter;
1022

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

1628
public function getFilters()
1729
{
18-
return array(
19-
new \Twig_SimpleFilter('ansi_to_html', array($this, 'ansiToHtml'), array('is_safe' => array('html'))),
20-
);
30+
return [
31+
new TwigFilter('ansi_to_html', [$this, 'ansiToHtml'], ['is_safe' => ['html']]),
32+
];
2133
}
2234

2335
public function getFunctions()
2436
{
25-
return array(
26-
new \Twig_SimpleFunction('ansi_css', array($this, 'css'), array('is_safe' => array('css'))),
27-
);
37+
return [
38+
new TwigFunction('ansi_css', [$this, 'css'], ['is_safe' => ['css']]),
39+
];
2840
}
2941

3042
public function ansiToHtml($string)

0 commit comments

Comments
 (0)