Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

Commit6c8c93d

Browse files
committed
feature#46944 [Console] Add Ansi8 (256 color) support, improve true color (Ansi24) support detection (julien-boudry)
This PR was merged into the 6.2 branch.Discussion----------[Console] Add Ansi8 (256 color) support, improve true color (Ansi24) support detection| Q | A| ------------- | ---| Branch? | 6.2 for features| Bug fix? | no| New feature? | yes| Deprecations? |no| License | MIT* Refactor the detection code of the colorimetric capacities of the terminal (much easier to extend next)* Improve true color terminal detection according tohttps://github.com/termstandard/colors/* Add detection for 256 colors terminal (Ansi8 support) and add the conversion from true color (Ansi24) to it. This is especially useful for the Apple Terminal which is still deployed by default in 2022, but not only.Commits-------3f90257 [Console] PR#46944 - Adds support for Ansi8 (256 colors) and improves terminal support detection (Ansi4, Ansi8, Ansi24)
2 parents41ebf12 +3f90257 commit6c8c93d

File tree

7 files changed

+323
-58
lines changed

7 files changed

+323
-58
lines changed

‎src/Symfony/Component/Console/CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGELOG
22
=========
33

4+
6.2
5+
---
6+
7+
* Improve truecolor terminal detection in some cases
8+
* Add support for 256 color terminals (conversion from Ansi24 to Ansi8 if terminal is capable of it)
9+
410
6.1
511
---
612

‎src/Symfony/Component/Console/Color.php‎

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,7 @@ private function parseColor(string $color, bool $background = false): string
117117
}
118118

119119
if ('#' ===$color[0]) {
120-
$color =substr($color,1);
121-
122-
if (3 ===\strlen($color)) {
123-
$color =$color[0].$color[0].$color[1].$color[1].$color[2].$color[2];
124-
}
125-
126-
if (6 !==\strlen($color)) {
127-
thrownewInvalidArgumentException(sprintf('Invalid "%s" color.',$color));
128-
}
129-
130-
return ($background ?'4' :'3').$this->convertHexColorToAnsi(hexdec($color));
120+
return ($background ?'4' :'3').Terminal::getTermColorSupport()->convertFromHexToAnsiColorCode($color);
131121
}
132122

133123
if (isset(self::COLORS[$color])) {
@@ -140,41 +130,4 @@ private function parseColor(string $color, bool $background = false): string
140130

141131
thrownewInvalidArgumentException(sprintf('Invalid "%s" color; expected one of (%s).',$color,implode(',',array_merge(array_keys(self::COLORS),array_keys(self::BRIGHT_COLORS)))));
142132
}
143-
144-
privatefunctionconvertHexColorToAnsi(int$color):string
145-
{
146-
$r = ($color >>16) &255;
147-
$g = ($color >>8) &255;
148-
$b =$color &255;
149-
150-
// see https://github.com/termstandard/colors/ for more information about true color support
151-
if ('truecolor' !==getenv('COLORTERM')) {
152-
return (string)$this->degradeHexColorToAnsi($r,$g,$b);
153-
}
154-
155-
returnsprintf('8;2;%d;%d;%d',$r,$g,$b);
156-
}
157-
158-
privatefunctiondegradeHexColorToAnsi(int$r,int$g,int$b):int
159-
{
160-
if (0 ===round($this->getSaturation($r,$g,$b) /50)) {
161-
return0;
162-
}
163-
164-
return (round($b /255) <<2) | (round($g /255) <<1) |round($r /255);
165-
}
166-
167-
privatefunctiongetSaturation(int$r,int$g,int$b):int
168-
{
169-
$r =$r /255;
170-
$g =$g /255;
171-
$b =$b /255;
172-
$v =max($r,$g,$b);
173-
174-
if (0 ===$diff =$v -min($r,$g,$b)) {
175-
return0;
176-
}
177-
178-
return (int)$diff *100 /$v;
179-
}
180133
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespaceSymfony\Component\Console\Output;
13+
14+
useSymfony\Component\Asset\Exception\InvalidArgumentException;
15+
16+
/**
17+
* @author Fabien Potencier <fabien@symfony.com>
18+
* @author Julien Boudry <julien@condorcet.vote>
19+
*/
20+
enum AnsiColorMode
21+
{
22+
/**
23+
* Classical 4-bit Ansi colors, including 8 classical colors and 8 bright color. Output syntax is "ESC[${foreGroundColorcode};${backGroundColorcode}m"
24+
* Must be compatible with all terminals and it's the minimal version supported.
25+
*/
26+
case Ansi4;
27+
28+
/**
29+
* 8-bit Ansi colors (240 differents colors + 16 duplicate color codes, ensuring backward compatibility).
30+
* Output syntax is: "ESC[38;5;${foreGroundColorcode};48;5;${backGroundColorcode}m"
31+
* Should be compatible with most terminals.
32+
*/
33+
case Ansi8;
34+
35+
/**
36+
* 24-bit Ansi colors (RGB).
37+
* Output syntax is: "ESC[38;2;${foreGroundColorcodeRed};${foreGroundColorcodeGreen};${foreGroundColorcodeBlue};48;2;${backGroundColorcodeRed};${backGroundColorcodeGreen};${backGroundColorcodeBlue}m"
38+
* May be compatible with many modern terminals.
39+
*/
40+
case Ansi24;
41+
42+
/**
43+
* Converts an RGB hexadecimal color to the corresponding Ansi code.
44+
*/
45+
publicfunctionconvertFromHexToAnsiColorCode(string$hexColor):string
46+
{
47+
$hexColor =str_replace('#','',$hexColor);
48+
49+
if (3 ===\strlen($hexColor)) {
50+
$hexColor =$hexColor[0].$hexColor[0].$hexColor[1].$hexColor[1].$hexColor[2].$hexColor[2];
51+
}
52+
53+
if (6 !==\strlen($hexColor)) {
54+
thrownewInvalidArgumentException(sprintf('Invalid "#%s" color.',$hexColor));
55+
}
56+
57+
$color =hexdec($hexColor);
58+
59+
$r = ($color >>16) &255;
60+
$g = ($color >>8) &255;
61+
$b =$color &255;
62+
63+
returnmatch ($this) {
64+
self::Ansi4 => (string)$this->convertFromRGB($r,$g,$b),
65+
self::Ansi8 =>'8;5;'.((string)$this->convertFromRGB($r,$g,$b)),
66+
self::Ansi24 =>sprintf('8;2;%d;%d;%d',$r,$g,$b)
67+
};
68+
}
69+
70+
privatefunctionconvertFromRGB(int$r,int$g,int$b):int
71+
{
72+
returnmatch ($this) {
73+
self::Ansi4 =>$this->degradeHexColorToAnsi4($r,$g,$b),
74+
self::Ansi8 =>$this->degradeHexColorToAnsi8($r,$g,$b),
75+
default =>thrownewInvalidArgumentException("RGB cannot be converted to{$this->name}.")
76+
};
77+
}
78+
79+
privatefunctiondegradeHexColorToAnsi4(int$r,int$g,int$b):int
80+
{
81+
if (0 ===round($this->getSaturation($r,$g,$b) /50)) {
82+
return0;
83+
}
84+
85+
return (int) ((round($b /255) <<2) | (round($g /255) <<1) |round($r /255));
86+
}
87+
88+
privatefunctiongetSaturation(int$r,int$g,int$b):int
89+
{
90+
$r =$r /255;
91+
$g =$g /255;
92+
$b =$b /255;
93+
$v =max($r,$g,$b);
94+
95+
if (0 ===$diff =$v -min($r,$g,$b)) {
96+
return0;
97+
}
98+
99+
return (int) ((int)$diff *100 /$v);
100+
}
101+
102+
/**
103+
* Inspired from https://github.com/ajalt/colormath/blob/e464e0da1b014976736cf97250063248fc77b8e7/colormath/src/commonMain/kotlin/com/github/ajalt/colormath/model/Ansi256.kt code (MIT license).
104+
*/
105+
privatefunctiondegradeHexColorToAnsi8(int$r,int$g,int$b):int
106+
{
107+
if ($r ===$g &&$g ===$b) {
108+
if ($r <8) {
109+
return16;
110+
}
111+
112+
if ($r >248) {
113+
return231;
114+
}
115+
116+
return (int)round(($r -8) /247 *24) +232;
117+
}else {
118+
return16 +
119+
(36 * (int)round($r /255 *5)) +
120+
(6 * (int)round($g /255 *5)) +
121+
(int)round($b /255 *5);
122+
}
123+
}
124+
}

‎src/Symfony/Component/Console/Terminal.php‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,49 @@
1111

1212
namespaceSymfony\Component\Console;
1313

14+
useSymfony\Component\Console\Output\AnsiColorMode;
15+
1416
class Terminal
1517
{
1618
privatestatic ?int$width =null;
1719
privatestatic ?int$height =null;
1820
privatestatic ?bool$stty =null;
1921

22+
/**
23+
* About Ansi color types: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
24+
* For more information about true color support with terminals https://github.com/termstandard/colors/.
25+
*/
26+
publicstaticfunctiongetTermColorSupport():AnsiColorMode
27+
{
28+
// Try with $COLORTERM first
29+
if (\is_string($colorterm =getenv('COLORTERM'))) {
30+
$colorterm =strtolower($colorterm);
31+
32+
if (str_contains($colorterm,'truecolor')) {
33+
return AnsiColorMode::Ansi24;
34+
}
35+
36+
if (str_contains($colorterm,'256color')) {
37+
return AnsiColorMode::Ansi8;
38+
}
39+
}
40+
41+
// Try with $TERM
42+
if (\is_string($term =getenv('TERM'))) {
43+
$term =strtolower($term);
44+
45+
if (str_contains($term,'truecolor')) {
46+
return AnsiColorMode::Ansi24;
47+
}
48+
49+
if (str_contains($term,'256color')) {
50+
return AnsiColorMode::Ansi8;
51+
}
52+
}
53+
54+
return AnsiColorMode::Ansi4;
55+
}
56+
2057
/**
2158
* Gets the terminal width.
2259
*/

‎src/Symfony/Component/Console/Tests/ColorTest.php‎

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
class ColorTestextends TestCase
1818
{
19-
publicfunctiontestAnsiColors()
19+
publicfunctiontestAnsi4Colors()
2020
{
2121
$color =newColor();
2222
$this->assertSame('',$color->apply(''));
@@ -33,21 +33,27 @@ public function testAnsiColors()
3333

3434
publicfunctiontestTrueColors()
3535
{
36-
if ('truecolor' !==getenv('COLORTERM')) {
37-
$this->markTestSkipped('True color not supported.');
38-
}
36+
$colorterm =getenv('COLORTERM');
37+
putenv('COLORTERM=truecolor');
3938

40-
$color =newColor('#fff','#000');
41-
$this->assertSame("\033[38;2;255;255;255;48;2;0;0;0m\033[39;49m",$color->apply(''));
39+
try {
40+
$color =newColor('#fff','#000');
41+
$this->assertSame("\033[38;2;255;255;255;48;2;0;0;0m\033[39;49m",$color->apply(''));
4242

43-
$color =newColor('#ffffff','#000000');
44-
$this->assertSame("\033[38;2;255;255;255;48;2;0;0;0m\033[39;49m",$color->apply(''));
43+
$color =newColor('#ffffff','#000000');
44+
$this->assertSame("\033[38;2;255;255;255;48;2;0;0;0m\033[39;49m",$color->apply(''));
45+
}finally {
46+
(false !==$colorterm) ?putenv('COLORTERM='.$colorterm) :putenv('COLORTERM');
47+
}
4548
}
4649

47-
publicfunctiontestDegradedTrueColors()
50+
publicfunctiontestDegradedTrueColorsToAnsi4()
4851
{
4952
$colorterm =getenv('COLORTERM');
53+
$term =getenv('TERM');
54+
5055
putenv('COLORTERM=');
56+
putenv('TERM=');
5157

5258
try {
5359
$color =newColor('#f00','#ff0');
@@ -56,7 +62,28 @@ public function testDegradedTrueColors()
5662
$color =newColor('#c0392b','#f1c40f');
5763
$this->assertSame("\033[31;43m\033[39;49m",$color->apply(''));
5864
}finally {
59-
putenv('COLORTERM='.$colorterm);
65+
(false !==$colorterm) ?putenv('COLORTERM='.$colorterm) :putenv('COLORTERM');
66+
(false !==$term) ?putenv('TERM='.$term) :putenv('TERM');
67+
}
68+
}
69+
70+
publicfunctiontestDegradedTrueColorsToAnsi8()
71+
{
72+
$colorterm =getenv('COLORTERM');
73+
$term =getenv('TERM');
74+
75+
putenv('COLORTERM=');
76+
putenv('TERM=symfonyTest-256color');
77+
78+
try {
79+
$color =newColor('#f57255','#8993c0');
80+
$this->assertSame("\033[38;5;210;48;5;146m\033[39;49m",$color->apply(''));
81+
82+
$color =newColor('#000000','#ffffff');
83+
$this->assertSame("\033[38;5;16;48;5;231m\033[39;49m",$color->apply(''));
84+
}finally {
85+
(false !==$colorterm) ?putenv('COLORTERM='.$colorterm) :putenv('COLORTERM');
86+
(false !==$term) ?putenv('TERM='.$term) :putenv('TERM');
6087
}
6188
}
6289
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp