Skip to content
Permalink
Browse files

*Support for enabling/disabling color output on demand

*Honor NO_COLOR environment variable
  • Loading branch information...
silkfire committed Mar 17, 2019
1 parent b079010 commit 8a89c2930a2b7ff2c6beae67e6c00fe1d74189d5
Showing with 173 additions and 25 deletions.
  1. +90 −21 src/ConsoleExtensions.cs
  2. +4 −4 src/Pastel.csproj
  3. +79 −0 tests/Pastel.Tests/ColorTests.cs
@@ -8,19 +8,6 @@

public static class ConsoleExtensions
{

static ConsoleExtensions()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

var enable = GetConsoleMode(iStdOut, out var outConsoleMode)
&& SetConsoleMode(iStdOut, outConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN);
}
}


private const int STD_OUTPUT_HANDLE = -11;
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;
@@ -38,6 +25,92 @@ static ConsoleExtensions()
public static extern uint GetLastError();


private delegate string ColorFormat( string input, Color color);
private delegate string ColorFormatHex(string input, string hexColor);


private const string _formatString = "\u001b[{0};2;{1};{2};{3}m{4}\u001b[0m";
private const string _foregroundModifier = "38";
private const string _backgroundModifier = "48";


private static readonly Func<string, int> _parseHexColor = hc => int.Parse(hc.Replace("#", ""), NumberStyles.HexNumber);

private static readonly Func<string, Color, string, string> _colorFormat = (s, c, f) => string.Format(_formatString, f, c.R, c.G, c.B, s);
private static readonly Func<string, string, string, string> _colorHexFormat = (s, c, f) => _colorFormat(s, Color.FromArgb(_parseHexColor(c)), f);

private static readonly ColorFormat _noColorOutputFormat = (s, _) => s;
private static readonly ColorFormatHex _noColorOutputHexFormat = (s, _) => s;

private static readonly ColorFormat _foregroundColorFormat = (s, c) => _colorFormat( s, c, _foregroundModifier);
private static readonly ColorFormatHex _foregroundColorHexFormat = (s, c) => _colorHexFormat(s, c, _foregroundModifier);

private static readonly ColorFormat _backgroundColorFormat = (s, c) => _colorFormat( s, c, _backgroundModifier);
private static readonly ColorFormatHex _backgroundColorHexFormat = (s, c) => _colorHexFormat(s, c, _backgroundModifier);


private static ColorFormat _foregroundColorFormatFunc;
private static ColorFormatHex _foregroundColorHexFormatFunc;

private static ColorFormat _backgroundColorFormatFunc;
private static ColorFormatHex _backgroundColorHexFormatFunc;


static ConsoleExtensions()
{
if (Environment.GetEnvironmentVariable("NO_COLOR") == null)
{
Enable();


if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

var enable = GetConsoleMode(iStdOut, out var outConsoleMode)
&& SetConsoleMode(iStdOut, outConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN);
}
}
else
{
Disable();
}
}












/// <summary>
/// Enables any future console color output produced by Pastel.
/// </summary>
public static void Enable()
{
_foregroundColorFormatFunc = _foregroundColorFormat;
_foregroundColorHexFormatFunc = _foregroundColorHexFormat;

_backgroundColorFormatFunc = _backgroundColorFormat;
_backgroundColorHexFormatFunc = _backgroundColorHexFormat;
}

/// <summary>
/// Disables any future console color output produced by Pastel.
/// </summary>
public static void Disable()
{
_foregroundColorFormatFunc = _noColorOutputFormat;
_foregroundColorHexFormatFunc = _noColorOutputHexFormat;

_backgroundColorFormatFunc = _noColorOutputFormat;
_backgroundColorHexFormatFunc = _noColorOutputHexFormat;
}


/// <summary>
@@ -47,7 +120,7 @@ static ConsoleExtensions()
/// <param name="color">The color to use on the specified string.</param>
public static string Pastel(this string input, Color color)
{
return $"\u001b[38;2;{color.R};{color.G};{color.B}m{input}\u001b[0m";
return _foregroundColorFormatFunc(input, color);
}

/// <summary>
@@ -57,9 +130,7 @@ public static string Pastel(this string input, Color color)
/// <param name="hexColor">The color to use on the specified string.<para>Supported format: [#]RRGGBB.</para></param>
public static string Pastel(this string input, string hexColor)
{
var color = Color.FromArgb(int.Parse(hexColor.Replace("#", ""), NumberStyles.HexNumber));

return Pastel(input, color);
return _foregroundColorHexFormatFunc(input, hexColor);
}


@@ -71,7 +142,7 @@ public static string Pastel(this string input, string hexColor)
/// <param name="color">The color to use on the specified string.</param>
public static string PastelBg(this string input, Color color)
{
return $"\u001b[48;2;{color.R};{color.G};{color.B}m{input}\u001b[0m";
return _backgroundColorFormatFunc(input, color);
}

/// <summary>
@@ -81,9 +152,7 @@ public static string PastelBg(this string input, Color color)
/// <param name="hexColor">The color to use on the specified string.<para>Supported format: [#]RRGGBB.</para></param>
public static string PastelBg(this string input, string hexColor)
{
var color = Color.FromArgb(int.Parse(hexColor.Replace("#", ""), NumberStyles.HexNumber));

return PastelBg(input, color);
return _backgroundColorHexFormatFunc(input, hexColor);
}
}
}
@@ -7,10 +7,10 @@
<Description>A tiny utility class that makes colorizing console output a breeze.</Description>
<PackageLicenseUrl>https://github.com/silkfire/Pastel/blob/master/LICENSE</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/silkfire/Pastel</PackageProjectUrl>
<PackageTags>console colors ansi colorize</PackageTags>
<Version>1.1.0</Version>
<AssemblyVersion>1.1.0.0</AssemblyVersion>
<PackageReleaseNotes>1.1.0 Added support for background colors.</PackageReleaseNotes>
<PackageTags>console colors ansi colorize NO_COLOR</PackageTags>
<Version>1.2.0</Version>
<AssemblyVersion>1.2.0.0</AssemblyVersion>
<PackageReleaseNotes>1.2.0 Added support for NO_COLOR (https://no-color.org). Color output can now be enabled/disabled on demand.</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/silkfire/Pastel/master/img/logo.png</PackageIconUrl>
</PropertyGroup>

@@ -200,5 +200,84 @@ public void A_Given_Hex_Color_String_Should_Return_Same_Ansi_Output_String_Regar
Assert.Equal(outputAnsiColorString1, outputAnsiColorString2);
}
}


public class NoOutputColor
{
private const string _input = "input";


private void ColorOutputEnabledTest()
{
/////////////////
// ARRANGE
/////////

ConsoleExtensions.Enable();


/////////////////
// ACT
/////////

var outputAnsiColorString1 = _input.Pastel( Color.FromArgb(1, 1, 1));
var outputAnsiColorString2 = _input.Pastel( "#010101");
var outputAnsiColorString3 = _input.PastelBg(Color.FromArgb(1, 1, 1));
var outputAnsiColorString4 = _input.PastelBg("#010101");


/////////////////
// ASSERT
/////////

Assert.Equal($"\u001b[38;2;1;1;1m{_input}\u001b[0m", outputAnsiColorString1);
Assert.Equal($"\u001b[38;2;1;1;1m{_input}\u001b[0m", outputAnsiColorString2);
Assert.Equal($"\u001b[48;2;1;1;1m{_input}\u001b[0m", outputAnsiColorString3);
Assert.Equal($"\u001b[48;2;1;1;1m{_input}\u001b[0m", outputAnsiColorString4);
}


[Fact]
public void Output_Should_Honor_Current_State_When_Switching_Between_States()
{
// Enable color output

ColorOutputEnabledTest();


// Disable color output

/////////////////
// ARRANGE
/////////

ConsoleExtensions.Disable();


/////////////////
// ACT
/////////

var outputAnsiColorString1 = _input.Pastel( Color.FromArgb(1, 1, 1));
var outputAnsiColorString2 = _input.Pastel( "#010101");
var outputAnsiColorString3 = _input.PastelBg(Color.FromArgb(1, 1, 1));
var outputAnsiColorString4 = _input.PastelBg("#010101");


/////////////////
// ASSERT
/////////

Assert.Equal(_input, outputAnsiColorString1);
Assert.Equal(_input, outputAnsiColorString2);
Assert.Equal(_input, outputAnsiColorString3);
Assert.Equal(_input, outputAnsiColorString4);


// Re-enable color output

ColorOutputEnabledTest();
}
}
}
}

0 comments on commit 8a89c29

Please sign in to comment.
You can’t perform that action at this time.