Sdk: CLI (permanently) changes console color when an invalid command is typed

Created on 28 Nov 2016  路  8Comments  路  Source: dotnet/sdk

Steps to reproduce

Try to use a command that doesn't exist. e.g.: dotnet.exe migate

Expected behavior

An error message appears, maybe in a different color, and then console color is unchanged after that.

Actual behavior

Console color remains pink-ish.

Environment data

.NET Command Line Tools (1.0.0-preview4-004129)

Product Information:
 Version:            1.0.0-preview4-004129
 Commit SHA-1 hash:  325b849858

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.14393
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   E:\Tools\dotnet-dev-win-x64.latest\sdk\1.0.0-preview4-004129
Bug

Most helpful comment

Greetings!
I've worked in a way to fix this issue, however, I'd like to explain what the issue is here first.

The Problem

Given the example above, here is the same example in the easiest way to explain it:

<OriginalConsoleColor>
dotnet foo
<Bold><Red>No executable found matching command "dotnet-foo"<OriginalConsoleColor><NotBold>
C:\Users\scott>

Each tag represents a ForegroundColor change. The real issue is in AnsiColorExtensions

        public static string Bold(this string text)
        {
            return "\x1B[1m" + text + "\x1B[22m";
        }

The marker 22m turns off bold - according to the AnsiConsole.Write.

If you look at the color extensions:

        public static string Red(this string text)
        {
            return "\x1B[31m" + text + "\x1B[39m";
        }

They all use 39m which translates to setting the console foreground back to the original color before the program started.

Summary

Since Console.ForegroundColor is static, whatever the last state this value is set to when the program ends will be what the user sees. Therefore, anyone who uses the AnsiColorExensions.Bold() last as in the case of the error message, the result is what you'll see in the original post.

Possible Solution

I've gone through a few variations of fixing the issue and through that process I see a flaw with the AnsiColorExensions and AnsiConsole. Mainly that we cannot assume the <OriginalConsoleColor> is either <Bold> or <NotBold>. So using any actual color (excluding bold) in the AnsiColorExensions will result in writing a message out to the Console using the original color which could be a Bold or NotBold color, maybe that was intended? And using the Bold() extension was to ensure that the color would in fact be bold? The fact that color and boldness are not two different properties of the console complicates what is trying to be done. You have Green and DarkGreen, no property for bold.

That said, this is one of the ways to fix this:

        public static string Bold(this string text)
        {
            return "\x1B[1m" + text + "\x1B[39m";
        }
        public static string NotBold(this string text)
        {
            //This is the default but this option forces console windows
            return "\x1B[22m" + text + "\x1B[39m";
        }

Going back to my earlier statement, the original color of theConsole.ForegroundColor could be a bold or not bold color, maybe you want to ensure text will in fact be NotBold? In which case, the above changes will be explicit.

I have those changes and others to support it in my fork for your viewing. I was about to make a PR, but due to design changes and get feedback there, but it seems maybe discussing it here is better.

Hopefully this all makes sense!

Reece

All 8 comments

This sounds like duplicate of https://github.com/dotnet/cli/issues/4428, which was closed because @livarcocc could not reproduce it in preview2.

@mellinoe Which console are you using? I see you are on windows, but I can't repro this in powershell.

This was just using cmd.exe.

Here's the issue @livarcocc. If you have a foreground color that is "intense" like BRIGHT green or BRIGHT pink (not just regular) and then dotnet.exe changes it for an error, it FAILS to set it back WITH the intensity setting. You have to repro this at the cmd.exe prompt with a BRIGHT color.

I believe there is a problem in https://github.com/dotnet/cli/blob/3a567e595773b61650f2dcd1c7ec2b53b91cd7a8/src/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs#L34-L42 @stephentoub

It's using the Console.SetColor stuff but the Intensity flag is getting dropped https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes

image

Greetings!
I've worked in a way to fix this issue, however, I'd like to explain what the issue is here first.

The Problem

Given the example above, here is the same example in the easiest way to explain it:

<OriginalConsoleColor>
dotnet foo
<Bold><Red>No executable found matching command "dotnet-foo"<OriginalConsoleColor><NotBold>
C:\Users\scott>

Each tag represents a ForegroundColor change. The real issue is in AnsiColorExtensions

        public static string Bold(this string text)
        {
            return "\x1B[1m" + text + "\x1B[22m";
        }

The marker 22m turns off bold - according to the AnsiConsole.Write.

If you look at the color extensions:

        public static string Red(this string text)
        {
            return "\x1B[31m" + text + "\x1B[39m";
        }

They all use 39m which translates to setting the console foreground back to the original color before the program started.

Summary

Since Console.ForegroundColor is static, whatever the last state this value is set to when the program ends will be what the user sees. Therefore, anyone who uses the AnsiColorExensions.Bold() last as in the case of the error message, the result is what you'll see in the original post.

Possible Solution

I've gone through a few variations of fixing the issue and through that process I see a flaw with the AnsiColorExensions and AnsiConsole. Mainly that we cannot assume the <OriginalConsoleColor> is either <Bold> or <NotBold>. So using any actual color (excluding bold) in the AnsiColorExensions will result in writing a message out to the Console using the original color which could be a Bold or NotBold color, maybe that was intended? And using the Bold() extension was to ensure that the color would in fact be bold? The fact that color and boldness are not two different properties of the console complicates what is trying to be done. You have Green and DarkGreen, no property for bold.

That said, this is one of the ways to fix this:

        public static string Bold(this string text)
        {
            return "\x1B[1m" + text + "\x1B[39m";
        }
        public static string NotBold(this string text)
        {
            //This is the default but this option forces console windows
            return "\x1B[22m" + text + "\x1B[39m";
        }

Going back to my earlier statement, the original color of theConsole.ForegroundColor could be a bold or not bold color, maybe you want to ensure text will in fact be NotBold? In which case, the above changes will be explicit.

I have those changes and others to support it in my fork for your viewing. I was about to make a PR, but due to design changes and get feedback there, but it seems maybe discussing it here is better.

Hopefully this all makes sense!

Reece

I, for one, am thrilled you've root caused this. @livarcocc @stephentoub

Thanks.

Alternate Solution

Haven't tested this, but keeping all the code in place as it is today and on startup, set the color to what you want, or at least to the Non-Bold version of the color. And before the program exits, reset the color and boldness back to the user's choice.

Watched the community stand-up - interesting background on how this bug really 'BUGGED' @shanselman ;-)
Always interesting listening to community stand-ups, keep up the good work!

Was this page helpful?
0 / 5 - 0 ratings