An abstraction over System.Console
that adds new input and output methods, colors and advanced outputs like progress bars and menus. And everything is ansi supported so it works on legacy systems and terminals.
- ๐ High performance, Low memory usage and allocation
- ๐ชถ Very lightweight (No external dependencies)
- Easy to use (no need to learn a new syntax while still writing less boilerplate code)
- ๐พ Supports legacy ansi terminals (like Windows 7)
- ๐ฅ Complete NativeAOT compatibility
- Supports all major platforms (Windows, Linux, Mac)
- โ Uses original output pipes, so that your cli's can be piped properly.
dotnet add package PrettyConsole
Everything starts off with the using statements, I recommend using the Console
statically
using static PrettyConsole.Console; // Access to all Console methods
using PrettyConsole; // Access to the Color struct
PrettyConsole uses an equation inspired syntax to colorize text. The syntax is as follows:
WriteLine("Test" * Color.Red / Color.Blue);
i.e TEXT * FOREGROUND / BACKGROUND
Any the 2 colors can be played with just like a real equation, omit the foreground and the default will be used, same goes for the background.
The most basic method for outputting is Write
, which has multiple overloads:
Write(ColoredOutput);
Write(ReadOnlySpan<ColoredOutput>); // use collections expression for the compiler to inline the array
Write(ReadOnlySpan<char>, ConsoleColor); // no string allocation with ReadOnlySpan<char>
Write(ReadOnlySpan<char>, ConsoleColor, ConsoleColor);
Write(T, ConsoleColor); // no string allocation with T : ISpanFormattable
Write(T, ConsoleColor, ConsoleColor);
Write(T, ConsoleColor, ConsoleColor, ReadOnlySpan<char>, IFormatProvider?);
There are also overloads for WriteLine
and WriteError
, WriteError
has the same overloads exactly as Write
but uses the Error
stream, this means you can output metrics or whatever, and if you pipe the output to another cli, it will be displayed correctly. WriteLine
only has overloads for ColoredOutput
and ReadOnlySpan<ColoredOutput>
. But you can use Write
followed by NewLine
to bridge the gap.
These are the methods for reading user input:
string? ReadLine(); // ReadLine<string>
string? ReadLine(ReadOnlySpan<ColoredOutput>);
T? ReadLine<T>(ReadOnlySpan<ColoredOutput>); // T : IParsable<T>
T ReadLine<T>(ReadOnlySpan<ColoredOutput>, T @default); // @default will be returned if parsing fails
bool TryReadLine<T>(ReadOnlySpan<ColoredOutput>, out T?); // T : IParsable<T>
bool TryReadLine<T>(ReadOnlySpan<ColoredOutput>, T @default, out T); // @default will be returned if parsing fails
bool TryReadLine<TEnum>(ReadOnlySpan<ColoredOutput>, bool ignoreCase, out TEnum?); // TEnum : struct, Enum
bool TryReadLine<TEnum>(ReadOnlySpan<ColoredOutput>, bool ignoreCase, TEnum @default, out TEnum); // @default will be returned if parsing fails
I always recommend using TryReadLine
instead of ReadLine
as you need to maintain less null checks and the result,
especially with @default
is much more concise.
These are some special methods for inputs:
// These will wait for the user to press any key
void RequestAnyInput(string message = "Press any key to continue...");
void RequestAnyInput(ReadOnlySpan<ColoredOutput> output);
// These request confirmation by special input from user
bool Confirm(ReadOnlySpan<ColoredOutput> message); // uses the default values ["y", "yes"]
// the default values can also be used by you at Console.DefaultConfirmValues
bool Confirm(ReadOnlySpan<ColoredOutput> message, ReadOnlySpan<string> trueValues, bool emptyIsTrue = true);
To aid in rendering and building your own complex outputs, there are many methods that simplify some processes.
ClearNextLines(int lines); // clears the next lines
ClearNextLinesError(int lines); // clears the next lines for the error stream
NewLine(); // outputs a new line
NewLineError(); // outputs a new line for the error stream
SetColors(ConsoleColor foreground, ConsoleColor background); // sets the colors of the console output
ResetColors(); // resets the colors of the console output
int GetCurrentLine(); // returns the current line number
GoToLine(int line); // moves the cursor to the specified line
Combining ClearNextLines
with GoToLine
will enable you to efficiently use the same space in the console for continuous output, such as progress outputting, for some cases there are also built-in methods for this, more on that later.
// This method will essentially write a line, clear it, go back to same position
// This allows a form of text-only progress bar
void OverrideCurrentLine(ReadOnlySpan<ColoredOutput> output);
// This methods will write a character at a time, with a delay between each character
async Task TypeWrite(ColoredOutput output, int delay = TypeWriteDefaultDelay);
async Task TypeWriteLine(ColoredOutput output, int delay = TypeWriteDefaultDelay);
// This prints an index view of the list, allows the user to select by index
// returns the actual choice that corresponds to the index
string Selection<TList>(ReadOnlySpan<ColoredOutput> title, TList choices) where TList : IList<string> {}
// Same as selection but allows the user to select multiple indexes
// Separated by spaces, and returns an array of the actual choices that correspond to the indexes
string[] MultiSelection<TList>(ReadOnlySpan<ColoredOutput> title, TList choices) where TList : IList<string> {}
// This prints a tree menu of 2 levels, allows the user to select the index
// Of the first and second level and returns the corresponding choices
(string option, string subOption) TreeMenu<TList>(ReadOnlySpan<ColoredOutput> title,
Dictionary<string, TList> menu) where TList : IList<string> {}
// This prints a table with headers, and columns for each list
void Table<TList>(TList headers, ReadOnlySpan<TList> columns) where TList : IList<string> {}
There are two types of progress bars here, they both are implemented using a class to maintain states.
var prg = new IndeterminateProgressBar(); // this setups the internal states
// Then you need to provide either a Task or Task</T>, the progress bar binds to it and runs until the task completes
await prg.RunAsync(task, "Running...", cancellationToken); // There are also overloads without header
// if the task is not started before being passed to the progress bar, it will be started automatically
// It is even better this way to synchronize the runtime of the progress bar with the task
ProgressBar
is a more powerful version, but requires a percentage of progress.
// ProgressBar implements IDisposable
var prg = new ProgressBar();
// then on each time the progress percentage is actually changed, you call Update
Update(percentage, ReadOnlySpan<char> header);
// There are also overloads without header, and percentage can be either int or double (0-100)
// Also, you can change some of the visual properties of the progress bar after initialization
// by using the properties of the ProgressBar class
prg.ProgressChar = 'โ '; // Character to fill the progress bar
prg.ForegroundColor = Color.Red; // Color of the empty part
prg.ProgressColor = Color.Blue; // The color of the filled part
Console
wraps over System.Console
and uses its In
, Out
, and Error
streams. Since the names of the classes are identical, combining them in usage is somewhat painful as the compiler doesn't know which overloads to use. To aid in most cases,
Console
exposes those streams as static properties, and you can use them directly.
In rare cases, you will need something that there is in System.Console
but not in Console
, such as ReadKey
, or SetCursorPosition
, some events or otherwise, then you can simply call System.Console
, this added verbosity is a worthy trade-off.
This project uses an MIT license, if you want to contribute, you can do so by forking the repository and creating a pull request.
If you have feature requests or bug reports, please create an issue.
For bug reports, feature requests or offers of support/sponsorship contact [email protected]
This project is proudly made in Israel ๐ฎ๐ฑ for the benefit of mankind.