From c97dd446eb2aafd65e8bd5a9531c6d9713510aae Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 10 Mar 2017 09:18:38 -0800 Subject: [PATCH] Simplify console output and fix color output on CMD --- shared/IConsole.cs | 2 + shared/PhysicalConsole.cs | 7 ++ shared/Reporting/ColorFormatter.cs | 57 --------------- shared/Reporting/CompositeFormatter.cs | 26 ------- shared/Reporting/ConditionalFormatter.cs | 24 ------- shared/Reporting/ConsoleReporter.cs | 72 +++++++++++++++++++ shared/Reporting/DefaultFormatter.cs | 14 ---- shared/Reporting/FormatterBuilder.cs | 46 ------------ shared/Reporting/FormattingReporter.cs | 62 ---------------- shared/Reporting/IFormatter.cs | 10 --- shared/Reporting/PrefixFormatter.cs | 20 ------ shared/Reporting/ReporterBuilder.cs | 65 ----------------- .../PrefixConsoleReporter.cs | 32 +++++++++ src/Microsoft.DotNet.Watcher.Tools/Program.cs | 47 +----------- .../Program.cs | 31 +------- .../Program.cs | 30 +------- ...porterTests.cs => ConsoleReporterTests.cs} | 36 +++------- test/Shared/TestConsole.cs | 5 ++ 18 files changed, 131 insertions(+), 455 deletions(-) delete mode 100644 shared/Reporting/ColorFormatter.cs delete mode 100644 shared/Reporting/CompositeFormatter.cs delete mode 100644 shared/Reporting/ConditionalFormatter.cs create mode 100644 shared/Reporting/ConsoleReporter.cs delete mode 100644 shared/Reporting/DefaultFormatter.cs delete mode 100644 shared/Reporting/FormatterBuilder.cs delete mode 100644 shared/Reporting/FormattingReporter.cs delete mode 100644 shared/Reporting/IFormatter.cs delete mode 100644 shared/Reporting/PrefixFormatter.cs delete mode 100644 shared/Reporting/ReporterBuilder.cs create mode 100644 src/Microsoft.DotNet.Watcher.Tools/PrefixConsoleReporter.cs rename test/Microsoft.Extensions.Tools.Tests/{ReporterTests.cs => ConsoleReporterTests.cs} (68%) diff --git a/shared/IConsole.cs b/shared/IConsole.cs index 20bb36e..7216c26 100644 --- a/shared/IConsole.cs +++ b/shared/IConsole.cs @@ -15,5 +15,7 @@ public interface IConsole bool IsInputRedirected { get; } bool IsOutputRedirected { get; } bool IsErrorRedirected { get; } + ConsoleColor ForegroundColor { get; set; } + void ResetColor(); } } diff --git a/shared/PhysicalConsole.cs b/shared/PhysicalConsole.cs index bdfb60a..9a93323 100644 --- a/shared/PhysicalConsole.cs +++ b/shared/PhysicalConsole.cs @@ -25,5 +25,12 @@ private PhysicalConsole() public bool IsInputRedirected => Console.IsInputRedirected; public bool IsOutputRedirected => Console.IsOutputRedirected; public bool IsErrorRedirected => Console.IsErrorRedirected; + public ConsoleColor ForegroundColor + { + get => Console.ForegroundColor; + set => Console.ForegroundColor = value; + } + + public void ResetColor() => Console.ResetColor(); } } diff --git a/shared/Reporting/ColorFormatter.cs b/shared/Reporting/ColorFormatter.cs deleted file mode 100644 index 5675d5b..0000000 --- a/shared/Reporting/ColorFormatter.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.Tools.Internal -{ - public class ColorFormatter : IFormatter - { - // resets foreground color only - private const string ResetColor = "\x1B[39m"; - - private static readonly IDictionary AnsiColorCodes - = new Dictionary - { - {ConsoleColor.Black, 30}, - {ConsoleColor.DarkRed, 31}, - {ConsoleColor.DarkGreen, 32}, - {ConsoleColor.DarkYellow, 33}, - {ConsoleColor.DarkBlue, 34}, - {ConsoleColor.DarkMagenta, 35}, - {ConsoleColor.DarkCyan, 36}, - {ConsoleColor.Gray, 37}, - {ConsoleColor.DarkGray, 90}, - {ConsoleColor.Red, 91}, - {ConsoleColor.Green, 92}, - {ConsoleColor.Yellow, 93}, - {ConsoleColor.Blue, 94}, - {ConsoleColor.Magenta, 95}, - {ConsoleColor.Cyan, 96}, - {ConsoleColor.White, 97}, - }; - - private readonly string _prefix; - - public ColorFormatter(ConsoleColor color) - { - _prefix = GetAnsiCode(color); - } - - public string Format(string text) - => text?.Length > 0 - ? $"{_prefix}{text}{ResetColor}" - : text; - - private static string GetAnsiCode(ConsoleColor color) - { - int code; - if (!AnsiColorCodes.TryGetValue(color, out code)) - { - throw new ArgumentOutOfRangeException(nameof(color), color, null); - } - return $"\x1B[{code}m"; - } - } -} \ No newline at end of file diff --git a/shared/Reporting/CompositeFormatter.cs b/shared/Reporting/CompositeFormatter.cs deleted file mode 100644 index 63aa074..0000000 --- a/shared/Reporting/CompositeFormatter.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.Extensions.Tools.Internal -{ - public class CompositeFormatter : IFormatter - { - private readonly IFormatter[] _formatters; - - public CompositeFormatter(IFormatter[] formatters) - { - Ensure.NotNull(formatters, nameof(formatters)); - _formatters = formatters; - } - - public string Format(string text) - { - foreach (var formatter in _formatters) - { - text = formatter.Format(text); - } - - return text; - } - } -} \ No newline at end of file diff --git a/shared/Reporting/ConditionalFormatter.cs b/shared/Reporting/ConditionalFormatter.cs deleted file mode 100644 index b0881f2..0000000 --- a/shared/Reporting/ConditionalFormatter.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.Extensions.Tools.Internal -{ - public class ConditionalFormatter : IFormatter - { - private readonly Func _predicate; - - public ConditionalFormatter(Func predicate) - { - Ensure.NotNull(predicate, nameof(predicate)); - - _predicate = predicate; - } - - public string Format(string text) - => _predicate() - ? text - : null; - } -} \ No newline at end of file diff --git a/shared/Reporting/ConsoleReporter.cs b/shared/Reporting/ConsoleReporter.cs new file mode 100644 index 0000000..ffcd1e8 --- /dev/null +++ b/shared/Reporting/ConsoleReporter.cs @@ -0,0 +1,72 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace Microsoft.Extensions.Tools.Internal +{ + public class ConsoleReporter : IReporter + { + private object _writeLock = new object(); + + public ConsoleReporter(IConsole console) + : this(console, verbose: false, quiet: false) + { } + + public ConsoleReporter(IConsole console, bool verbose, bool quiet) + { + Ensure.NotNull(console, nameof(console)); + + Console = console; + IsVerbose = verbose; + IsQuiet = quiet; + } + + protected IConsole Console { get; } + public bool IsVerbose { get; set; } + public bool IsQuiet { get; set; } + + protected virtual void WriteLine(TextWriter writer, string message, ConsoleColor? color) + { + lock (_writeLock) + { + if (color.HasValue) + { + Console.ForegroundColor = color.Value; + } + + writer.WriteLine(message); + + if (color.HasValue) + { + Console.ResetColor(); + } + } + } + + public virtual void Error(string message) + => WriteLine(Console.Error, message, ConsoleColor.Red); + public virtual void Warn(string message) + => WriteLine(Console.Out, message, ConsoleColor.Yellow); + + public virtual void Output(string message) + { + if (IsQuiet) + { + return; + } + WriteLine(Console.Out, message, color: null); + } + + public virtual void Verbose(string message) + { + if (!IsVerbose) + { + return; + } + + WriteLine(Console.Out, message, ConsoleColor.DarkGray); + } + } +} diff --git a/shared/Reporting/DefaultFormatter.cs b/shared/Reporting/DefaultFormatter.cs deleted file mode 100644 index 4d4348b..0000000 --- a/shared/Reporting/DefaultFormatter.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.Extensions.Tools.Internal -{ - public class DefaultFormatter : IFormatter - { - public static readonly IFormatter Instance = new DefaultFormatter(); - - private DefaultFormatter() {} - public string Format(string text) - => text; - } -} \ No newline at end of file diff --git a/shared/Reporting/FormatterBuilder.cs b/shared/Reporting/FormatterBuilder.cs deleted file mode 100644 index 3d34914..0000000 --- a/shared/Reporting/FormatterBuilder.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.Tools.Internal -{ - public class FormatterBuilder - { - private readonly List _formatters = new List(); - - public FormatterBuilder WithColor(ConsoleColor color) - { - _formatters.Add(new ColorFormatter(color)); - return this; - } - - public FormatterBuilder WithPrefix(string prefix) - { - _formatters.Add(new PrefixFormatter(prefix)); - return this; - } - - public FormatterBuilder When(Func predicate) - { - _formatters.Add(new ConditionalFormatter(predicate)); - return this; - } - - public IFormatter Build() - { - if (_formatters.Count == 0) - { - return DefaultFormatter.Instance; - } - - if (_formatters.Count == 1) - { - return _formatters[0]; - } - - return new CompositeFormatter(_formatters.ToArray()); - } - } -} \ No newline at end of file diff --git a/shared/Reporting/FormattingReporter.cs b/shared/Reporting/FormattingReporter.cs deleted file mode 100644 index 0c49c0d..0000000 --- a/shared/Reporting/FormattingReporter.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; - -namespace Microsoft.Extensions.Tools.Internal -{ - public class FormattingReporter : IReporter - { - private readonly object _writelock = new object(); - private readonly IConsole _console; - private readonly IFormatter _verbose; - private readonly IFormatter _warn; - private readonly IFormatter _output; - private readonly IFormatter _error; - - public FormattingReporter(IConsole console, - IFormatter verbose, - IFormatter output, - IFormatter warn, - IFormatter error) - { - Ensure.NotNull(console, nameof(console)); - Ensure.NotNull(verbose, nameof(verbose)); - Ensure.NotNull(output, nameof(output)); - Ensure.NotNull(warn, nameof(warn)); - Ensure.NotNull(error, nameof(error)); - - _console = console; - _verbose = verbose; - _output = output; - _warn = warn; - _error = error; - } - - - public void Verbose(string message) - => Write(_console.Out, _verbose.Format(message)); - - public void Output(string message) - => Write(_console.Out, _output.Format(message)); - - public void Warn(string message) - => Write(_console.Out, _warn.Format(message)); - - public void Error(string message) - => Write(_console.Error, _error.Format(message)); - - private void Write(TextWriter writer, string message) - { - if (message == null) - { - return; - } - - lock (_writelock) - { - writer.WriteLine(message); - } - } - } -} \ No newline at end of file diff --git a/shared/Reporting/IFormatter.cs b/shared/Reporting/IFormatter.cs deleted file mode 100644 index 9094728..0000000 --- a/shared/Reporting/IFormatter.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.Extensions.Tools.Internal -{ - public interface IFormatter - { - string Format(string text); - } -} \ No newline at end of file diff --git a/shared/Reporting/PrefixFormatter.cs b/shared/Reporting/PrefixFormatter.cs deleted file mode 100644 index b9602c4..0000000 --- a/shared/Reporting/PrefixFormatter.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.Extensions.Tools.Internal -{ - public class PrefixFormatter : IFormatter - { - private readonly string _prefix; - - public PrefixFormatter(string prefix) - { - Ensure.NotNullOrEmpty(prefix, nameof(prefix)); - - _prefix = prefix; - } - - public string Format(string text) - => _prefix + text; - } -} \ No newline at end of file diff --git a/shared/Reporting/ReporterBuilder.cs b/shared/Reporting/ReporterBuilder.cs deleted file mode 100644 index 9283642..0000000 --- a/shared/Reporting/ReporterBuilder.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.Extensions.Tools.Internal -{ - public class ReporterBuilder - { - private readonly FormatterBuilder _verbose = new FormatterBuilder(); - private readonly FormatterBuilder _output = new FormatterBuilder(); - private readonly FormatterBuilder _warn = new FormatterBuilder(); - private readonly FormatterBuilder _error = new FormatterBuilder(); - private IConsole _console; - - public ReporterBuilder WithConsole(IConsole console) - { - _console = console; - return this; - } - - public FormatterBuilder Verbose() => _verbose; - public FormatterBuilder Output() => _output; - public FormatterBuilder Warn() => _warn; - public FormatterBuilder Error() => _error; - - public ReporterBuilder Verbose(Action configure) - { - configure(_verbose); - return this; - } - - public ReporterBuilder Output(Action configure) - { - configure(_output); - return this; - } - - public ReporterBuilder Warn(Action configure) - { - configure(_warn); - return this; - } - - public ReporterBuilder Error(Action configure) - { - configure(_error); - return this; - } - - public IReporter Build() - { - if (_console == null) - { - throw new InvalidOperationException($"Cannot build without first calling {nameof(WithConsole)}"); - } - - return new FormattingReporter(_console, - _verbose.Build(), - _output.Build(), - _warn.Build(), - _error.Build()); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Watcher.Tools/PrefixConsoleReporter.cs b/src/Microsoft.DotNet.Watcher.Tools/PrefixConsoleReporter.cs new file mode 100644 index 0000000..b245327 --- /dev/null +++ b/src/Microsoft.DotNet.Watcher.Tools/PrefixConsoleReporter.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using Microsoft.Extensions.Tools.Internal; + +namespace Microsoft.DotNet.Watcher +{ + public class PrefixConsoleReporter : ConsoleReporter + { + private object _lock = new object(); + + public PrefixConsoleReporter(IConsole console, bool verbose, bool quiet) + : base(console, verbose, quiet) + { } + + protected override void WriteLine(TextWriter writer, string message, ConsoleColor? color) + { + const string prefix = "watch : "; + + lock (_lock) + { + Console.ForegroundColor = ConsoleColor.DarkGray; + writer.Write(prefix); + Console.ResetColor(); + + base.WriteLine(writer, message, color); + } + } + } +} diff --git a/src/Microsoft.DotNet.Watcher.Tools/Program.cs b/src/Microsoft.DotNet.Watcher.Tools/Program.cs index 9d18757..261a92f 100644 --- a/src/Microsoft.DotNet.Watcher.Tools/Program.cs +++ b/src/Microsoft.DotNet.Watcher.Tools/Program.cs @@ -176,51 +176,6 @@ private async Task ListFilesAsync( } private static IReporter CreateReporter(bool verbose, bool quiet, IConsole console) - { - const string prefix = "watch : "; - var colorPrefix = new ColorFormatter(ConsoleColor.DarkGray).Format(prefix); - - return new ReporterBuilder() - .WithConsole(console) - .Verbose(f => - { - if (console.IsOutputRedirected) - { - f.WithPrefix(prefix); - } - else - { - f.WithColor(ConsoleColor.DarkGray).WithPrefix(colorPrefix); - } - - f.When(() => verbose || CliContext.IsGlobalVerbose()); - }) - .Output(f => f - .WithPrefix(console.IsOutputRedirected ? prefix : colorPrefix) - .When(() => !quiet)) - .Warn(f => - { - if (console.IsOutputRedirected) - { - f.WithPrefix(prefix); - } - else - { - f.WithColor(ConsoleColor.Yellow).WithPrefix(colorPrefix); - } - }) - .Error(f => - { - if (console.IsOutputRedirected) - { - f.WithPrefix(prefix); - } - else - { - f.WithColor(ConsoleColor.Red).WithPrefix(colorPrefix); - } - }) - .Build(); - } + => new PrefixConsoleReporter(console, verbose || CliContext.IsGlobalVerbose(), quiet); } } diff --git a/src/Microsoft.Extensions.Caching.SqlConfig.Tools/Program.cs b/src/Microsoft.Extensions.Caching.SqlConfig.Tools/Program.cs index 190335a..874244f 100644 --- a/src/Microsoft.Extensions.Caching.SqlConfig.Tools/Program.cs +++ b/src/Microsoft.Extensions.Caching.SqlConfig.Tools/Program.cs @@ -99,34 +99,7 @@ public int Run(string[] args) } private IReporter CreateReporter(bool verbose) - { - return new ReporterBuilder() - .WithConsole(_console) - .Verbose(f => - { - f.When(() => verbose); - if (!_console.IsOutputRedirected) - { - f.WithColor(ConsoleColor.DarkGray); - } - }) - .Warn(f => - { - if (!_console.IsOutputRedirected) - { - f.WithColor(ConsoleColor.Yellow); - } - }) - .Error(f => - { - if (!_console.IsErrorRedirected) - { - f.WithColor(ConsoleColor.Red); - } - }) - .Build(); - } - + => new ConsoleReporter(_console, verbose, quiet: false); private int CreateTableAndIndexes(IReporter reporter) { ValidateConnectionString(); @@ -197,4 +170,4 @@ private void ValidateConnectionString() } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.Extensions.SecretManager.Tools/Program.cs b/src/Microsoft.Extensions.SecretManager.Tools/Program.cs index 667c16b..187e401 100644 --- a/src/Microsoft.Extensions.SecretManager.Tools/Program.cs +++ b/src/Microsoft.Extensions.SecretManager.Tools/Program.cs @@ -89,33 +89,7 @@ internal int RunInternal(params string[] args) } private IReporter CreateReporter(bool verbose) - { - return new ReporterBuilder() - .WithConsole(_console) - .Verbose(f => - { - f.When(() => verbose); - if (!_console.IsOutputRedirected) - { - f.WithColor(ConsoleColor.DarkGray); - } - }) - .Warn(f => - { - if (!_console.IsOutputRedirected) - { - f.WithColor(ConsoleColor.Yellow); - } - }) - .Error(f => - { - if (!_console.IsErrorRedirected) - { - f.WithColor(ConsoleColor.Red); - } - }) - .Build(); - } + => new ConsoleReporter(_console, verbose, quiet: false); internal string ResolveId(CommandLineOptions options, IReporter reporter) { @@ -128,4 +102,4 @@ internal string ResolveId(CommandLineOptions options, IReporter reporter) return resolver.Resolve(options.Project, options.Configuration); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.Extensions.Tools.Tests/ReporterTests.cs b/test/Microsoft.Extensions.Tools.Tests/ConsoleReporterTests.cs similarity index 68% rename from test/Microsoft.Extensions.Tools.Tests/ReporterTests.cs rename to test/Microsoft.Extensions.Tools.Tests/ConsoleReporterTests.cs index 5c6cde5..34cfd42 100644 --- a/test/Microsoft.Extensions.Tools.Tests/ReporterTests.cs +++ b/test/Microsoft.Extensions.Tools.Tests/ConsoleReporterTests.cs @@ -13,30 +13,11 @@ public class ReporterTests { private static readonly string EOL = Environment.NewLine; - [Theory] - [InlineData(ConsoleColor.DarkGray, "\x1B[90m")] - [InlineData(ConsoleColor.Red, "\x1B[91m")] - [InlineData(ConsoleColor.Yellow, "\x1B[93m")] - public void WrapsWithAnsiColorCode(ConsoleColor color, string code) - { - Assert.Equal($"{code}sample\x1B[39m", new ColorFormatter(color).Format("sample")); - } - - [Fact] - public void SkipsColorCodesForEmptyOrNullInput() - { - var formatter = new ColorFormatter(ConsoleColor.Blue); - Assert.Empty(formatter.Format(string.Empty)); - Assert.Null(formatter.Format(null)); - } - [Fact] public void WritesToStandardStreams() { var testConsole = new TestConsole(); - var reporter = new FormattingReporter(testConsole, - DefaultFormatter.Instance, DefaultFormatter.Instance, - DefaultFormatter.Instance, DefaultFormatter.Instance); + var reporter = new ConsoleReporter(testConsole, verbose: true, quiet: false); // stdout reporter.Verbose("verbose"); @@ -57,13 +38,6 @@ public void WritesToStandardStreams() testConsole.Clear(); } - [Fact] - public void FailsToBuildWithoutConsole() - { - Assert.Throws( - () => new ReporterBuilder().Build()); - } - private class TestConsole : IConsole { private readonly StringBuilder _out; @@ -92,12 +66,18 @@ public void Clear() _error.Clear(); } + public void ResetColor() + { + ForegroundColor = default(ConsoleColor); + } + public TextWriter Out { get; } public TextWriter Error { get; } public TextReader In { get; } public bool IsInputRedirected { get; } public bool IsOutputRedirected { get; } public bool IsErrorRedirected { get; } + public ConsoleColor ForegroundColor { get; set; } } } -} \ No newline at end of file +} diff --git a/test/Shared/TestConsole.cs b/test/Shared/TestConsole.cs index fd98ff5..6804c02 100644 --- a/test/Shared/TestConsole.cs +++ b/test/Shared/TestConsole.cs @@ -27,6 +27,7 @@ public TestConsole(ITestOutputHelper output) public bool IsInputRedirected { get; set; } = false; public bool IsOutputRedirected { get; } = false; public bool IsErrorRedirected { get; } = false; + public ConsoleColor ForegroundColor { get; set; } public ConsoleCancelEventArgs ConsoleCancelKey() { @@ -39,6 +40,10 @@ public ConsoleCancelEventArgs ConsoleCancelKey() return args; } + public void ResetColor() + { + } + private class TestOutputWriter : TextWriter { private readonly ITestOutputHelper _output;